AppendYouTubeDateGraphMLAttributeValue
    (
        XmlNode oXmlNodeToSelectFrom,
        String sXPath,
        XmlNamespaceManager oXmlNamespaceManager,
        GraphMLXmlDocument oGraphMLXmlDocument,
        XmlNode oVertexXmlNode,
        String sGraphMLAttributeID
    )
    {
        Debug.Assert(oXmlNodeToSelectFrom != null);
        Debug.Assert( !String.IsNullOrEmpty(sXPath) );
        Debug.Assert(oGraphMLXmlDocument != null);
        Debug.Assert(oVertexXmlNode != null);
        Debug.Assert( !String.IsNullOrEmpty(sGraphMLAttributeID) );
        AssertValid();

        String sYouTubeDate;

        if ( XmlUtil2.TrySelectSingleNodeAsString(oXmlNodeToSelectFrom,
            sXPath, oXmlNamespaceManager, out sYouTubeDate) )
        {
            oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode,
                sGraphMLAttributeID, FormatYouTubeDate(sYouTubeDate) );

            return (true);
        }

        return (false);
    }
        GetTwitterSearchNetworkConfiguration
        (
            out String searchTerm,
            out TwitterSearchNetworkAnalyzer.WhatToInclude whatToInclude,
            out Int32 maximumPeoplePerRequest,
            out String networkFileFolderPath,
            out NetworkFileFormats networkFileFormats,
            out Boolean automateNodeXLWorkbook
        )
        {
            AssertValid();
            Debug.Assert(m_oNetworkConfigurationXmlDocument != null);
            Debug.Assert(GetNetworkType() == NetworkType.TwitterSearch);

            XmlNode oTwitterSearchNetworkConfigurationNode =
                XmlUtil2.SelectRequiredSingleNode(
                    m_oNetworkConfigurationXmlDocument,
                    "/NetworkConfiguration/TwitterSearchNetworkConfiguration",
                    null);

            searchTerm = XmlUtil2.SelectRequiredSingleNodeAsString(
                oTwitterSearchNetworkConfigurationNode, "SearchTerm/text()", null);

            whatToInclude =
                GetRequiredEnumValue <TwitterSearchNetworkAnalyzer.WhatToInclude>(
                    oTwitterSearchNetworkConfigurationNode, "WhatToInclude/text()",
                    "WhatToInclude");

            GetTwitterCommonConfiguration(oTwitterSearchNetworkConfigurationNode,
                                          out maximumPeoplePerRequest, out networkFileFolderPath,
                                          out networkFileFormats, out automateNodeXLWorkbook);
        }
예제 #3
0
        GetGraphDirectedness
        (
            XmlNode oGraphXmlNode
        )
        {
            Debug.Assert(oGraphXmlNode != null);
            AssertValid();

            String sEdgeDefault = XmlUtil2.SelectRequiredSingleNodeAsString(
                oGraphXmlNode, "@edgedefault", null);

            switch (sEdgeDefault)
            {
            case "directed":

                return(GraphDirectedness.Directed);

            case "undirected":

                return(GraphDirectedness.Undirected);

            default:

                throw new XmlException(
                          "The \"edgedefault\" attribute on the \"graph\" XML node"
                          + " must be either \"directed\" or \"undirected\"."
                          );
            }
        }
예제 #4
0
        GetVertexCount
        (
            XmlDocument oGraphMLXmlDocument
        )
        {
            Debug.Assert(oGraphMLXmlDocument != null);
            AssertValid();

            Int32   iVertexCount = 0;
            XmlNode oXmlNode;

            XmlNamespaceManager oXmlNamespaceManager =
                GraphMLXmlDocument.CreateXmlNamespaceManager(
                    oGraphMLXmlDocument, "g");

            if (XmlUtil2.TrySelectSingleNode(oGraphMLXmlDocument,
                                             "g:graphml/g:graph/g:node", oXmlNamespaceManager,
                                             out oXmlNode))
            {
                while (oXmlNode != null)
                {
                    if (oXmlNode.Name == "node")
                    {
                        iVertexCount++;
                    }

                    oXmlNode = oXmlNode.NextSibling;
                }
            }

            return(iVertexCount);
        }
예제 #5
0
        FlickrScreenNameToUserID
        (
            String sScreenNameAnyCase,
            String sApiKey,
            RequestStatistics oRequestStatistics,
            out String sUserID,
            out String sScreenNameCorrectCase
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sScreenNameAnyCase));
            Debug.Assert(!String.IsNullOrEmpty(sApiKey));
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            XmlDocument oXmlDocument = GetXmlDocument(

                GetFlickrMethodUrl("flickr.people.findByUsername", sApiKey,
                                   "&username="******"rsp/user/@nsid", null);

            sScreenNameCorrectCase = XmlUtil2.SelectRequiredSingleNodeAsString(
                oXmlDocument, "rsp/user/username/text()", null);
        }
예제 #6
0
        AppendDoubleGraphMLAttributeValue
        (
            XmlNode oXmlNodeToSelectFrom,
            String sXPath,
            XmlNamespaceManager oXmlNamespaceManager,
            GraphMLXmlDocument oGraphMLXmlDocument,
            XmlNode oEdgeOrVertexXmlNode,
            String sGraphMLAttributeID
        )
        {
            Debug.Assert(oXmlNodeToSelectFrom != null);
            Debug.Assert(!String.IsNullOrEmpty(sXPath));
            Debug.Assert(oGraphMLXmlDocument != null);
            Debug.Assert(oEdgeOrVertexXmlNode != null);
            Debug.Assert(!String.IsNullOrEmpty(sGraphMLAttributeID));
            AssertValid();

            Double dAttributeValue;

            if (XmlUtil2.TrySelectSingleNodeAsDouble(oXmlNodeToSelectFrom, sXPath,
                                                     oXmlNamespaceManager, out dAttributeValue))
            {
                oGraphMLXmlDocument.AppendGraphMLAttributeValue(
                    oEdgeOrVertexXmlNode, sGraphMLAttributeID, dAttributeValue);

                return(true);
            }

            return(false);
        }
예제 #7
0
        GetCommentersEnumerator
        (
            String sUserID,
            Int32 iMaximumPerRequest,
            Boolean bSkipMostPage1Errors,
            String sApiKey,
            RequestStatistics oRequestStatistics
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sUserID));
            Debug.Assert(iMaximumPerRequest > 0);
            Debug.Assert(!String.IsNullOrEmpty(sApiKey));
            Debug.Assert(oRequestStatistics != null);

            AssertValid();

            // Get the user's public photos, which are paged.

            String sUrl = GetFlickrMethodUrl("flickr.people.getPublicPhotos",
                                             sApiKey, GetUserIDUrlParameter(sUserID));

            foreach (XmlNode oPhotoXmlNode in EnumerateXmlNodes(sUrl,
                                                                "rsp/photos/photo", iMaximumPerRequest, bSkipMostPage1Errors,
                                                                oRequestStatistics))
            {
                String sPhotoID;

                if (!XmlUtil2.TrySelectSingleNodeAsString(oPhotoXmlNode, "@id",
                                                          null, out sPhotoID))
                {
                    continue;
                }

                // Get the photo's comments, which are not paged.

                ReportProgress(String.Format(

                                   "Getting comments for the photo \"{0}\"."
                                   ,
                                   sPhotoID
                                   ));

                sUrl = GetFlickrMethodUrl("flickr.photos.comments.getList",
                                          sApiKey, "&photo_id=" + UrlUtil.EncodeUrlParameter(sPhotoID)
                                          );

                XmlDocument oXmlDocument;

                if (TryGetXmlDocument(sUrl, oRequestStatistics,
                                      out oXmlDocument))
                {
                    foreach (XmlNode oCommentXmlNode in oXmlDocument.SelectNodes(
                                 "rsp/comments/comment"))
                    {
                        yield return(oCommentXmlNode);
                    }
                }
            }
        }
예제 #8
0
        ParseEdges
        (
            IGraph oGraph,
            XmlNode oGraphXmlNode,
            XmlNamespaceManager oXmlNamespaceManager,
            Dictionary <String, IVertex> oVertexDictionary,
            Dictionary <String, GraphMLAttribute> oGraphMLAttributeDictionary
        )
        {
            Debug.Assert(oGraph != null);
            Debug.Assert(oGraphXmlNode != null);
            Debug.Assert(oXmlNamespaceManager != null);
            Debug.Assert(oVertexDictionary != null);
            Debug.Assert(oGraphMLAttributeDictionary != null);
            AssertValid();

            IEdgeCollection oEdges = oGraph.Edges;

            Boolean bGraphIsDirected =
                (oGraph.Directedness == GraphDirectedness.Directed);

            foreach (XmlNode oEdgeXmlNode in oGraphXmlNode.SelectNodes(
                         GraphMLPrefix + ":edge", oXmlNamespaceManager))
            {
                IVertex oVertex1, oVertex2;

                if (
                    !TryGraphMLNodeIDToVertex(oEdgeXmlNode, "source",
                                              oVertexDictionary, out oVertex1)
                    ||
                    !TryGraphMLNodeIDToVertex(oEdgeXmlNode, "target",
                                              oVertexDictionary, out oVertex2)
                    )
                {
                    // From the GraphML Primer:
                    //
                    // For applications which can not handle nested graphs the
                    // fall-back behaviour is to ignore nodes which are not
                    // contained in the top-level graph and to ignore edges which
                    // have do not have both endpoints in the top-level graph.

                    continue;
                }

                IEdge oEdge = oEdges.Add(oVertex1, oVertex2, bGraphIsDirected);

                String sID;

                if (XmlUtil2.TrySelectSingleNodeAsString(oEdgeXmlNode, "@id",
                                                         null, out sID))
                {
                    oEdge.Name = sID;
                }

                ParseGraphMLAttributeValues(oEdgeXmlNode, oXmlNamespaceManager,
                                            oEdge, false, oGraphMLAttributeDictionary);
            }
        }
    YouTubeErrorResponseToMessage
    (
        HttpWebResponse oHttpWebResponse 
    )
    {
        Debug.Assert(oHttpWebResponse != null);
        AssertValid();

        // YouTube provides error information as an XmlDocument.

        String sMessage = null;
        XmlDocument oXmlDocument = new XmlDocument();
        Stream oStream = null;

        try
        {
            oStream = oHttpWebResponse.GetResponseStream();
            oXmlDocument.Load(oStream);
        }
        catch (XmlException)
        {
            return (null);
        }
        finally
        {
            if (oStream != null)
            {
                oStream.Close();
            }
        }

        XmlNamespaceManager oXmlNamespaceManager =
            YouTubeUserNetworkAnalyzer.CreateXmlNamespaceManager(oXmlDocument);

        // Although the document may contain multiple error nodes, just look at
        // the first one for now.

        if ( XmlUtil2.TrySelectSingleNodeAsString(oXmlDocument,
            "gd:errors/gd:error/gd:code/text()", oXmlNamespaceManager,
            out sMessage) )
        {
            if (sMessage.IndexOf("too_many_recent_calls") >= 0)
            {
                sMessage =
                    "You have made too many YouTube network requests recently."
                    + "  Wait a few minutes and try again."
                    ;
            }
        }
        else
        {
            sMessage = null;
        }

        return (sMessage);
    }
예제 #10
0
        GetScreenNameFromVertexXmlNode
        (
            XmlNode oVertexXmlNode
        )
        {
            Debug.Assert(oVertexXmlNode != null);
            AssertValid();

            return(XmlUtil2.SelectRequiredSingleNodeAsString(oVertexXmlNode,
                                                             "@id", null));
        }
        GetCommonConfiguration
        (
            XmlNode oParentNode,
            out String sNetworkFileFolderPath
        )
        {
            Debug.Assert(oParentNode != null);
            AssertValid();

            sNetworkFileFolderPath = XmlUtil2.SelectRequiredSingleNodeAsString(
                oParentNode, "NetworkFileFolder/text()", null);
        }
        GetRequiredEnumValue <T>
        (
            XmlNode oNode,
            String sXPath,
            String[] asEnumValuesToIgnore,
            String sValueToUseIfEmpty,
            String sTagName
        )
        {
            Debug.Assert(oNode != null);
            Debug.Assert(!String.IsNullOrEmpty(sXPath));
            Debug.Assert(sValueToUseIfEmpty != null);
            Debug.Assert(!String.IsNullOrEmpty(sTagName));
            AssertValid();

            Exception oException;

            try
            {
                String sEnumAsString = XmlUtil2.SelectRequiredSingleNodeAsString(
                    oNode, sXPath, null);

                if (asEnumValuesToIgnore != null)
                {
                    sEnumAsString = RemoveEnumValues(
                        sEnumAsString, asEnumValuesToIgnore);

                    if (sEnumAsString.Length == 0)
                    {
                        sEnumAsString = sValueToUseIfEmpty;
                    }
                }

                return((T)Enum.Parse(typeof(T), sEnumAsString));
            }
            catch (XmlException oXmlException)
            {
                oException = oXmlException;
            }
            catch (ArgumentException oArgumentException)
            {
                oException = oArgumentException;
            }

            String sErrorMessage = String.Format(
                "The {0} value is missing or invalid."
                ,
                sTagName
                );

            throw new XmlException(sErrorMessage, oException);
        }
예제 #13
0
        GetXmlDocument
        (
            String sUrl,
            RequestStatistics oRequestStatistics
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sUrl));
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            XmlDocument oXmlDocument = GetXmlDocumentWithRetries(sUrl,
                                                                 HttpStatusCodesToFailImmediately, oRequestStatistics);

            String sStatus;

            if (
                XmlUtil2.TrySelectSingleNodeAsString(oXmlDocument, "rsp/@stat",
                                                     null, out sStatus)
                &&
                sStatus == "ok"
                )
            {
                return(oXmlDocument);
            }

            // Flickr indicates errors by returning an XML document containing an
            // rsp/err node.  The following code turns such an error document into
            // a custom FlickrException.

            String sErrorMessage;

            if (XmlUtil2.TrySelectSingleNodeAsString(oXmlDocument, "rsp/err/@msg",
                                                     null, out sErrorMessage))
            {
                if (sErrorMessage.ToLower().IndexOf("user not found") >= 0)
                {
                    sErrorMessage =
                        "The user wasn't found.  Either there is no such user, or"
                        + " she has hidden herself from public searches."
                    ;
                }
            }
            else
            {
                sErrorMessage =
                    "Flickr provided information in an unrecognized format.";
            }

            throw new FlickrException(sErrorMessage);
        }
예제 #14
0
        ParseVertices
        (
            IGraph oGraph,
            XmlNode oGraphXmlNode,
            XmlNamespaceManager oXmlNamespaceManager,
            Dictionary <String, GraphMLAttribute> oGraphMLAttributeDictionary
        )
        {
            Debug.Assert(oGraph != null);
            Debug.Assert(oGraphXmlNode != null);
            Debug.Assert(oXmlNamespaceManager != null);
            Debug.Assert(oGraphMLAttributeDictionary != null);
            AssertValid();

            IVertexCollection oVertices = oGraph.Vertices;

            // The key is the id attribute of the "node" XML node, and the value is
            // the corresponding IVertex.

            Dictionary <String, IVertex> oVertexDictionary =
                new Dictionary <String, IVertex>();

            foreach (XmlNode oNodeXmlNode in oGraphXmlNode.SelectNodes(
                         GraphMLPrefix + ":node", oXmlNamespaceManager))
            {
                String sID = XmlUtil2.SelectRequiredSingleNodeAsString(
                    oNodeXmlNode, "@id", null);

                IVertex oVertex = oVertices.Add();
                oVertex.Name = sID;

                try
                {
                    oVertexDictionary.Add(sID, oVertex);
                }
                catch (ArgumentException)
                {
                    throw new XmlException(
                              "The id \"" + sID + "\" exists for two \"node\" XML nodes."
                              + "  Node id values must be unique."
                              );
                }

                ParseGraphMLAttributeValues(oNodeXmlNode, oXmlNamespaceManager,
                                            oVertex, true, oGraphMLAttributeDictionary);
            }

            return(oVertexDictionary);
        }
예제 #15
0
        OnNetworkObtained
        (
            XmlDocument oGraphMLXmlDocument,
            RequestStatistics oRequestStatistics,
            String sNetworkDescription,
            String sNetworkTitle,
            String sPartialFileName
        )
        {
            Debug.Assert(oGraphMLXmlDocument != null);
            Debug.Assert(oRequestStatistics != null);
            Debug.Assert(!String.IsNullOrEmpty(sNetworkDescription));
            Debug.Assert(!String.IsNullOrEmpty(sNetworkTitle));
            Debug.Assert(!String.IsNullOrEmpty(sPartialFileName));
            AssertValid();

            XmlNode oGraphXmlNode = XmlUtil2.SelectRequiredSingleNode(
                oGraphMLXmlDocument, "g:graphml/g:graph",

                GraphMLXmlDocument.CreateXmlNamespaceManager(
                    oGraphMLXmlDocument, "g")
                );

            XmlUtil2.SetAttributes(oGraphXmlNode,
                                   "description", sNetworkDescription);

            XmlUtil2.SetAttributes(oGraphXmlNode,
                                   "suggestedTitle", sNetworkTitle);

            String sSuggestedFileNameNoExtension = String.Format(
                "{0} NodeXL {1}"
                ,
                DateTimeUtil2.ToCultureInvariantFileName(
                    oRequestStatistics.StartTimeUtc),

                sPartialFileName
                );

            XmlUtil2.SetAttributes(oGraphXmlNode,
                                   "suggestedFileNameNoExtension", sSuggestedFileNameNoExtension);

            if (oRequestStatistics.UnexpectedExceptions > 0)
            {
                // The network is partial.

                throw new PartialNetworkException(oGraphMLXmlDocument,
                                                  oRequestStatistics);
            }
        }
예제 #16
0
        LoadGraphCore
        (
            Stream stream
        )
        {
            Debug.Assert(stream != null);
            AssertValid();

            XmlDocument oXmlDocument = new XmlDocument();

            oXmlDocument.Load(stream);

            XmlNamespaceManager oXmlNamespaceManager = new XmlNamespaceManager(
                oXmlDocument.NameTable);

            oXmlNamespaceManager.AddNamespace(GraphMLPrefix, GraphMLUri);

            XmlNode oGraphMLXmlNode = oXmlDocument.DocumentElement;

            XmlNode oGraphXmlNode = XmlUtil2.SelectRequiredSingleNode(
                oGraphMLXmlNode, GraphMLPrefix + ":graph", oXmlNamespaceManager);

            // Parse the vertex and edge attribute definitions.
            //
            // The key is the id attribute of a "key" XML node, and the value is
            // the corresponding GraphMLAttribute object.

            Dictionary <String, GraphMLAttribute> oGraphMLAttributeDictionary =
                ParseGraphMLAttributeDefinitions(oGraphMLXmlNode,
                                                 oXmlNamespaceManager);

            GraphDirectedness eGraphDirectedness =
                GetGraphDirectedness(oGraphXmlNode);

            IGraph oGraph = new Graph(eGraphDirectedness);

            // The key is the id attribute of the "node" XML node, and the value is
            // the corresponding IVertex.

            Dictionary <String, IVertex> oVertexDictionary = ParseVertices(oGraph,
                                                                           oGraphXmlNode, oXmlNamespaceManager, oGraphMLAttributeDictionary);

            ParseEdges(oGraph, oGraphXmlNode, oXmlNamespaceManager,
                       oVertexDictionary, oGraphMLAttributeDictionary);

            SaveGraphMLAttributeNames(oGraph, oGraphMLAttributeDictionary);

            return(oGraph);
        }
        TryGetSampleImageUrl
        (
            String sTag,
            String sApiKey,
            RequestStatistics oRequestStatistics,
            out String sSampleImageUrl
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sTag));
            Debug.Assert(!String.IsNullOrEmpty(sApiKey));
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            sSampleImageUrl = null;

            String sUrl = GetFlickrMethodUrl("flickr.tags.getClusterPhotos",
                                             sApiKey, "&tag=" + UrlUtil.EncodeUrlParameter(sTag));

            XmlDocument oXmlDocument;
            String      sPhotoID;

            if (
                !TryGetXmlDocument(sUrl, oRequestStatistics, out oXmlDocument)
                ||
                !XmlUtil2.TrySelectSingleNodeAsString(oXmlDocument,
                                                      "rsp/photos/photo/@id", null, out sPhotoID)
                )
            {
                return(false);
            }

            sUrl = GetFlickrMethodUrl("flickr.photos.getSizes", sApiKey,
                                      "&photo_id=" + UrlUtil.EncodeUrlParameter(sPhotoID));

            if (
                !TryGetXmlDocument(sUrl, oRequestStatistics, out oXmlDocument)
                ||
                !XmlUtil2.TrySelectSingleNodeAsString(oXmlDocument,
                                                      "rsp/sizes/size[@label='Thumbnail']/@source", null,
                                                      out sSampleImageUrl)
                )
            {
                return(false);
            }

            return(true);
        }
        GetTwitterCommonConfiguration
        (
            XmlNode oParentNode,
            out Int32 iMaximumPeoplePerRequest,
            out String sNetworkFileFolderPath,
            out NetworkFileFormats eNetworkFileFormats,
            out Boolean bAutomateNodeXLWorkbook
        )
        {
            Debug.Assert(oParentNode != null);
            AssertValid();

            String sMaximumPeoplePerRequest;

            iMaximumPeoplePerRequest = Int32.MaxValue;

            if (XmlUtil2.TrySelectSingleNodeAsString(oParentNode,
                                                     "MaximumPeoplePerRequest/text()", null,
                                                     out sMaximumPeoplePerRequest))
            {
                if (!Int32.TryParse(sMaximumPeoplePerRequest,
                                    out iMaximumPeoplePerRequest))
                {
                    throw new XmlException(
                              "The MaximumPeoplePerRequest value is not valid."
                              );
                }
            }

            sNetworkFileFolderPath = XmlUtil2.SelectRequiredSingleNodeAsString(
                oParentNode, "NetworkFileFolder/text()", null);

            eNetworkFileFormats = GetRequiredEnumValue <NetworkFileFormats>(
                oParentNode, "NetworkFileFormats/text()", "NetworkFileFormats");

            // The AutomateNodeXLWorkbook node was added in a later version of the
            // program, so it is not required.

            if (!XmlUtil2.TrySelectSingleNodeAsBoolean(
                    oParentNode, "AutomateNodeXLWorkbook/text()", null,
                    out bAutomateNodeXLWorkbook))
            {
                bAutomateNodeXLWorkbook = false;
            }
        }
예제 #19
0
        TryGraphMLNodeIDToVertex
        (
            XmlNode oEdgeXmlNode,
            String sSourceOrTarget,
            Dictionary <String, IVertex> oVertexDictionary,
            out IVertex oVertex
        )
        {
            Debug.Assert(oEdgeXmlNode != null);
            Debug.Assert(!String.IsNullOrEmpty(sSourceOrTarget));
            Debug.Assert(oVertexDictionary != null);
            AssertValid();

            String sID = XmlUtil2.SelectRequiredSingleNodeAsString(oEdgeXmlNode,
                                                                   "@" + sSourceOrTarget, null);

            return(oVertexDictionary.TryGetValue(sID, out oVertex));
        }
예제 #20
0
        ParseGraphAttribute
        (
            IGraph oGraph,
            XmlNode oGraphXmlNode,
            String sXmlAttributeName,
            String sKey
        )
        {
            Debug.Assert(oGraph != null);
            Debug.Assert(oGraphXmlNode != null);
            Debug.Assert(!String.IsNullOrEmpty(sXmlAttributeName));
            Debug.Assert(!String.IsNullOrEmpty(sKey));
            AssertValid();

            String sValue;

            if (XmlUtil2.TrySelectSingleNodeAsString(oGraphXmlNode,
                                                     "@" + sXmlAttributeName, null, out sValue))
            {
                oGraph.SetValue(sKey, sValue);
            }
        }
예제 #21
0
        TryGetEdgeGraphMLAttributeValue
        (
            XmlNode oEdgeXmlNode,
            String sAttributeID,
            XmlNamespaceManager oXmlNamespaceManager,
            out String sAttributeValue
        )
        {
            Debug.Assert(oEdgeXmlNode != null);
            Debug.Assert(!String.IsNullOrEmpty(sAttributeID));
            Debug.Assert(oXmlNamespaceManager != null);

            String sXPath = String.Format(

                "g:data[@key=\"{0}\"]/text()"
                ,
                sAttributeID
                );

            return(XmlUtil2.TrySelectSingleNodeAsString(
                       oEdgeXmlNode, sXPath, oXmlNamespaceManager, out sAttributeValue));
        }
        GetRequiredEnumValue <T>
        (
            XmlNode oNode,
            String sXPath,
            String sTagName
        )
        {
            Debug.Assert(oNode != null);
            Debug.Assert(!String.IsNullOrEmpty(sXPath));
            Debug.Assert(!String.IsNullOrEmpty(sTagName));
            AssertValid();

            Exception oException;

            try
            {
                String sText = XmlUtil2.SelectRequiredSingleNodeAsString(oNode,
                                                                         sXPath, null);

                return((T)Enum.Parse(typeof(T), sText));
            }
            catch (XmlException oXmlException)
            {
                oException = oXmlException;
            }
            catch (ArgumentException oArgumentException)
            {
                oException = oArgumentException;
            }

            String sErrorMessage = String.Format(
                "The {0} value is missing or invalid."
                ,
                sTagName
                );

            throw new XmlException(sErrorMessage, oException);
        }
예제 #23
0
        GetAttributeValue
        (
            XmlNode dataXmlNode
        )
        {
            Debug.Assert(dataXmlNode != null);
            Debug.Assert(dataXmlNode.Name == "data");
            AssertValid();

            String sKey = XmlUtil2.SelectRequiredSingleNodeAsString(dataXmlNode,
                                                                    "@key", null);

            Debug.Assert(sKey == m_sID);

            String sAttributeValue;

            if (!XmlUtil2.TrySelectSingleNodeAsString(dataXmlNode, "text()",
                                                      null, out sAttributeValue))
            {
                // Allow missing inner text for GraphML-attributes of type string.
                // This was found in a GraphML file created by the yED program.

                sAttributeValue = String.Empty;
            }

            try
            {
                return(ConvertAttributeValue(sAttributeValue));
            }
            catch (FormatException)
            {
                throw new XmlException(
                          "The GraphML-attribute value specified for a \"data\" XML node"
                          + " with the key \"" + sKey + "\" is not of the specified"
                          + " type."
                          );
            }
        }
예제 #24
0
        ParseGraphMLAttributeValues
        (
            XmlNode oNodeOrEdgeXmlNode,
            XmlNamespaceManager oXmlNamespaceManager,
            IMetadataProvider oEdgeOrVertex,
            Boolean bIsVertex,
            Dictionary <String, GraphMLAttribute> oGraphMLAttributeDictionary
        )
        {
            Debug.Assert(oNodeOrEdgeXmlNode != null);
            Debug.Assert(oXmlNamespaceManager != null);
            Debug.Assert(oEdgeOrVertex != null);
            Debug.Assert(oGraphMLAttributeDictionary != null);
            AssertValid();

            // For each GraphML-attribute that has a default value, set a metadata
            // value on the edge or vertex.  This value may may get overwritten
            // later.

            foreach (GraphMLAttribute oGraphMLAttribute in
                     oGraphMLAttributeDictionary.Values)
            {
                if (oGraphMLAttribute.IsForVertex == bIsVertex)
                {
                    Object oDefaultAttributeValue;

                    if (oGraphMLAttribute.TryGetDefaultAttributeValue(
                            out oDefaultAttributeValue))
                    {
                        oEdgeOrVertex.SetValue(oGraphMLAttribute.Name,
                                               oDefaultAttributeValue);
                    }
                }
            }

            // For each "data" XML node specified for this edge or vertex, set
            // a metadata value on the edge or vertex.  This may overwrite a
            // previously-written default value.

            foreach (XmlNode oDataXmlNode in oNodeOrEdgeXmlNode.SelectNodes(
                         GraphMLPrefix + ":data", oXmlNamespaceManager))
            {
                String sGraphMLAttributeID =
                    XmlUtil2.SelectRequiredSingleNodeAsString(oDataXmlNode, "@key",
                                                              null);

                GraphMLAttribute oGraphMLAttribute;

                if (!oGraphMLAttributeDictionary.TryGetValue(sGraphMLAttributeID,
                                                             out oGraphMLAttribute))
                {
                    throw new XmlException(
                              "A \"data\" XML node has the key \"" + sGraphMLAttributeID
                              + "\", for which there is no corresponding \"key\" XML"
                              + " node."
                              );
                }

                oEdgeOrVertex.SetValue(oGraphMLAttribute.Name,
                                       oGraphMLAttribute.GetAttributeValue(oDataXmlNode));
            }
        }
        AppendVertexXmlNodes
        (
            String sSearchTerm,
            WhatToInclude eWhatToInclude,
            Int32 iMaximumVideos,
            GraphMLXmlDocument oGraphMLXmlDocument,
            RequestStatistics oRequestStatistics,
            out HashSet <String> oVideoIDs,
            out Dictionary <String, LinkedList <String> > oCategoryDictionary
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sSearchTerm));
            Debug.Assert(iMaximumVideos > 0);
            Debug.Assert(oGraphMLXmlDocument != null);
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            ReportProgress("Getting a list of videos.");

            // This is used to skip duplicate videos in the results returned by
            // YouTube.  (I'm not sure why YouTube sometimes returns duplicates,
            // but it does.)

            oVideoIDs = new HashSet <String>();

            // If an edge should be included for each pair of videos that share the
            // same category, the key is a lower-case category and the value is a
            // LinkedList of the video IDs that have the category.

            if (WhatToIncludeFlagIsSet(eWhatToInclude,
                                       WhatToInclude.SharedCategoryEdges))
            {
                oCategoryDictionary =
                    new Dictionary <String, LinkedList <String> >();
            }
            else
            {
                oCategoryDictionary = null;
            }

            String sUrl = String.Format(

                "http://gdata.youtube.com/feeds/api/videos?q={0}"
                ,
                EncodeUrlParameter(sSearchTerm)
                );

            // The document consists of an "entry" XML node for each video.

            foreach (XmlNode oEntryXmlNode in EnumerateXmlNodes(sUrl,
                                                                "a:feed/a:entry", iMaximumVideos, false, oRequestStatistics))
            {
                XmlNamespaceManager oXmlNamespaceManager =
                    CreateXmlNamespaceManager(oEntryXmlNode.OwnerDocument);

                // Use the video ID as the GraphML vertex name.  The video title
                // can't be used because it is not unique.

                String sVideoID;

                if (
                    !XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode,
                                                          "media:group/yt:videoid/text()", oXmlNamespaceManager,
                                                          out sVideoID)
                    ||
                    oVideoIDs.Contains(sVideoID)
                    )
                {
                    continue;
                }

                oVideoIDs.Add(sVideoID);

                XmlNode oVertexXmlNode = oGraphMLXmlDocument.AppendVertexXmlNode(
                    sVideoID);

                AppendStringGraphMLAttributeValue(oEntryXmlNode,
                                                  "a:title/text()", oXmlNamespaceManager, oGraphMLXmlDocument,
                                                  oVertexXmlNode, TitleID);

                AppendStringGraphMLAttributeValue(oEntryXmlNode,
                                                  "a:author/a:name/text()", oXmlNamespaceManager,
                                                  oGraphMLXmlDocument, oVertexXmlNode, AuthorID);

                AppendDoubleGraphMLAttributeValue(oEntryXmlNode,
                                                  "gd:rating/@average", oXmlNamespaceManager,
                                                  oGraphMLXmlDocument, oVertexXmlNode, RatingID);

                AppendInt32GraphMLAttributeValue(oEntryXmlNode,
                                                 "yt:statistics/@viewCount", oXmlNamespaceManager,
                                                 oGraphMLXmlDocument, oVertexXmlNode, ViewsID);

                AppendInt32GraphMLAttributeValue(oEntryXmlNode,
                                                 "yt:statistics/@favoriteCount", oXmlNamespaceManager,
                                                 oGraphMLXmlDocument, oVertexXmlNode, FavoritedID);

                AppendInt32GraphMLAttributeValue(oEntryXmlNode,
                                                 "gd:comments/gd:feedLink/@countHint", oXmlNamespaceManager,
                                                 oGraphMLXmlDocument, oVertexXmlNode, CommentsID);

                AppendYouTubeDateGraphMLAttributeValue(oEntryXmlNode,
                                                       "a:published/text()", oXmlNamespaceManager,
                                                       oGraphMLXmlDocument, oVertexXmlNode, CreatedDateUtcID);

                AppendStringGraphMLAttributeValue(oEntryXmlNode,
                                                  "media:group/media:thumbnail/@url", oXmlNamespaceManager,
                                                  oGraphMLXmlDocument, oVertexXmlNode,
                                                  NodeXLGraphMLUtil.VertexImageFileID);

                if (AppendStringGraphMLAttributeValue(oEntryXmlNode,
                                                      "media:group/media:player/@url", oXmlNamespaceManager,
                                                      oGraphMLXmlDocument, oVertexXmlNode,
                                                      NodeXLGraphMLUtil.VertexMenuActionID))
                {
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode,
                                                                    NodeXLGraphMLUtil.VertexMenuTextID,
                                                                    "Play Video in Browser");
                }

                if (oCategoryDictionary != null)
                {
                    CollectCategories(oEntryXmlNode, sVideoID,
                                      oXmlNamespaceManager, oCategoryDictionary);
                }
            }
        }
        AppendSharedResponderEdges
        (
            GraphMLXmlDocument oGraphMLXmlDocument,
            HashSet <String> oVideoIDs,
            Int32 iMaximumResponses,
            String sUrlPattern,
            String sResponderTitle,
            String sKeyAttributeID,
            RequestStatistics oRequestStatistics
        )
        {
            Debug.Assert(oGraphMLXmlDocument != null);
            Debug.Assert(oVideoIDs != null);
            Debug.Assert(iMaximumResponses > 0);
            Debug.Assert(!String.IsNullOrEmpty(sUrlPattern));
            Debug.Assert(sUrlPattern.IndexOf("{0}") >= 0);
            Debug.Assert(!String.IsNullOrEmpty(sResponderTitle));
            Debug.Assert(!String.IsNullOrEmpty(sKeyAttributeID));
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            // The key is the name of an author and the value is a LinkedList of
            // the video IDs to which the author has responded.

            Dictionary <String, LinkedList <String> > oAuthorUserNameDictionary =
                new Dictionary <String, LinkedList <String> >();

            foreach (String sVideoID in oVideoIDs)
            {
                ReportProgress(String.Format(

                                   "Getting {0}s for the video with the ID \"{1}\"."
                                   ,
                                   sResponderTitle,
                                   sVideoID
                                   ));

                // This is to prevent self-loop edges that would result when the
                // same author responds to the same video more than once.

                HashSet <String> oAuthorUserNames = new HashSet <String>();

                String sUrl = String.Format(sUrlPattern, sVideoID);

                // The document consists of an "entry" XML node for each response.

                foreach (XmlNode oEntryXmlNode in EnumerateXmlNodes(sUrl,
                                                                    "a:feed/a:entry", iMaximumResponses, true,
                                                                    oRequestStatistics))
                {
                    XmlNamespaceManager oXmlNamespaceManager =
                        CreateXmlNamespaceManager(oEntryXmlNode.OwnerDocument);

                    String sAuthorUserName;

                    if (
                        XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode,
                                                             "a:author/a:name/text()", oXmlNamespaceManager,
                                                             out sAuthorUserName)
                        &&
                        !oAuthorUserNames.Contains(sAuthorUserName)
                        )
                    {
                        AddVideoIDToDictionary(sAuthorUserName, sVideoID,
                                               oAuthorUserNameDictionary);

                        oAuthorUserNames.Add(sAuthorUserName);
                    }
                }
            }

            ReportProgress("Adding edges for shared " + sResponderTitle + "s.");

            AppendEdgesFromDictionary(oAuthorUserNameDictionary,
                                      oGraphMLXmlDocument, "Shared " + sResponderTitle, sKeyAttributeID);
        }
예제 #27
0
        ParseKeyXmlNode
        (
            XmlNode oKeyXmlNode,
            XmlNamespaceManager oXmlNamespaceManager,
            String sGraphMLPrefix
        )
        {
            Debug.Assert(oKeyXmlNode != null);
            Debug.Assert(oXmlNamespaceManager != null);
            Debug.Assert(!String.IsNullOrEmpty(sGraphMLPrefix));

            m_sID = XmlUtil2.SelectRequiredSingleNodeAsString(oKeyXmlNode,
                                                              "@id", null);

            String sFor = XmlUtil2.SelectRequiredSingleNodeAsString(oKeyXmlNode,
                                                                    "@for", null);

            // Note that the @attr.name and @attr.type attributes are optional.
            // Default to sensible values if they are missing.

            if (!XmlUtil2.TrySelectSingleNodeAsString(oKeyXmlNode,
                                                      "@attr.name", null, out m_sName))
            {
                m_sName = m_sID;
            }

            String sType;

            if (!XmlUtil2.TrySelectSingleNodeAsString(oKeyXmlNode, "@attr.type",
                                                      null, out sType))
            {
                sType = "string";
            }

            switch (sFor)
            {
            case "node":

                m_bIsForVertex = true;
                break;

            case "edge":

                m_bIsForVertex = false;
                break;

            // NodeXL doesn't support the "graph" or "all" values allowed by
            // the GraphML specification.  The caller should filter out such
            // "key" XML nodes before using this class.

            default:

                throw new XmlException(
                          "The \"for\" attribute on the \"key\" XML node with the"
                          + " id \"" + m_sID + "\" must be either \"node\" or"
                          + " \"edge\"."
                          );
            }

            switch (sType)
            {
            case "boolean":

                m_eType = AttributeType.Boolean;
                break;

            case "int":

                m_eType = AttributeType.Int;
                break;

            case "long":

                m_eType = AttributeType.Long;
                break;

            case "float":

                m_eType = AttributeType.Float;
                break;

            case "double":

                m_eType = AttributeType.Double;
                break;

            case "string":

                m_eType = AttributeType.String;
                break;

            default:

                throw new XmlException(
                          "The \"attr.type\" attribute on the \"key\" XML node with"
                          + " id \"" + m_sID + "\" does not have a valid value."
                          );
            }

            m_oDefaultAttributeValue = null;

            String sDefaultAttributeValue;

            XmlNode oDefaultXmlNode = oKeyXmlNode.SelectSingleNode(
                sGraphMLPrefix + ":default", oXmlNamespaceManager);

            if (
                oDefaultXmlNode != null
                &&
                XmlUtil2.TrySelectSingleNodeAsString(oDefaultXmlNode, "text()",
                                                     null, out sDefaultAttributeValue)
                )
            {
                try
                {
                    m_oDefaultAttributeValue =
                        ConvertAttributeValue(sDefaultAttributeValue);
                }
                catch (FormatException)
                {
                    throw new XmlException(
                              "The default value specified for the \"key\" XML node with"
                              + " the id \"" + m_sID + "\" is not of the specified type."
                              );
                }
            }

            AssertValid();
        }
        GetRelatedTagsRecursive
        (
            String sTag,
            WhatToInclude eWhatToInclude,
            NetworkLevel eNetworkLevel,
            String sApiKey,
            Int32 iRecursionLevel,
            GraphMLXmlDocument oGraphMLXmlDocument,
            Dictionary <String, XmlNode> oTagDictionary,
            RequestStatistics oRequestStatistics
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sTag));

            Debug.Assert(eNetworkLevel == NetworkLevel.One ||
                         eNetworkLevel == NetworkLevel.OnePointFive ||
                         eNetworkLevel == NetworkLevel.Two);

            Debug.Assert(!String.IsNullOrEmpty(sApiKey));
            Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2);
            Debug.Assert(oGraphMLXmlDocument != null);
            Debug.Assert(oTagDictionary != null);
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            /*
             * Here is what this method should do, based on the eNetworkLevel and
             * iRecursionLevel parameters.
             *
             *      eNetworkLevel
             *
             |One               | OnePointFive      | Two
             *  ---|------------------| ------------------| -----------------
             * i   1  |Add all vertices. | Add all vertices. | Add all vertices.
             * R      |                  |                   |
             * e      |Add all edges.    | Add all edges.    | Add all edges.
             * c      |                  |                   |
             * u      |Do not recurse.   | Recurse.          | Recurse.
             * r      |                  |                   |
             * s   ---|------------------|-------------------|------------------
             * i   2  |Impossible.       | Do not add        | Add all vertices.
             * o      |                  | vertices.         |
             * n      |                  |                   |
             * L      |                  | Add edges only if | Add all edges.
             * e      |                  | vertices are      |
             * v      |                  | already included. |
             * e      |                  |                   |
             * l      |                  | Do not recurse.   | Do not recurse.
             |                  |                   |
             |  ---|------------------|-------------------|------------------
             */

            Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel,
                                                      iRecursionLevel);

            Boolean bNeedToAppendVertices = GetNeedToAppendVertices(
                eNetworkLevel, iRecursionLevel);

            ReportProgress("Getting tags related to \"" + sTag + "\".");

            String sUrl = GetFlickrMethodUrl("flickr.tags.getRelated", sApiKey,
                                             "&tag=" + UrlUtil.EncodeUrlParameter(sTag));

            XmlDocument oXmlDocument;

            try
            {
                oXmlDocument = GetXmlDocument(sUrl, oRequestStatistics);
            }
            catch (Exception oException)
            {
                // If the exception is not a WebException or XmlException, or if
                // none of the network has been obtained yet, throw the exception.

                if (!HttpSocialNetworkUtil.ExceptionIsWebOrXml(oException) ||
                    !oGraphMLXmlDocument.HasVertexXmlNode)
                {
                    throw oException;
                }

                return;
            }

            // The document consists of a single "tags" node with zero or more
            // "tag" child nodes.

            String sOtherTag = null;

            XmlNodeList oTagNodes = oXmlDocument.DocumentElement.SelectNodes(
                "tags/tag");

            if (oTagNodes.Count > 0)
            {
                AppendVertexXmlNode(sTag, oGraphMLXmlDocument, oTagDictionary);
            }

            foreach (XmlNode oTagNode in oTagNodes)
            {
                sOtherTag = XmlUtil2.SelectRequiredSingleNodeAsString(oTagNode,
                                                                      "text()", null);

                if (bNeedToAppendVertices)
                {
                    AppendVertexXmlNode(sOtherTag, oGraphMLXmlDocument,
                                        oTagDictionary);
                }

                if (bNeedToAppendVertices ||
                    oTagDictionary.ContainsKey(sOtherTag))
                {
                    oGraphMLXmlDocument.AppendEdgeXmlNode(sTag, sOtherTag);
                }
            }

            if (bNeedToRecurse)
            {
                foreach (XmlNode oTagNode in oTagNodes)
                {
                    sOtherTag = XmlUtil2.SelectRequiredSingleNodeAsString(oTagNode,
                                                                          "text()", null);

                    GetRelatedTagsRecursive(sOtherTag, eWhatToInclude,
                                            eNetworkLevel, sApiKey, 2, oGraphMLXmlDocument,
                                            oTagDictionary, oRequestStatistics);
                }
            }
        }
        AppendAllStatisticGraphMLAttributeValues
        (
            String sUserName,
            XmlNode oVertexXmlNode,
            GraphMLXmlDocument oGraphMLXmlDocument,
            RequestStatistics oRequestStatistics
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sUserName));
            Debug.Assert(oVertexXmlNode != null);
            Debug.Assert(oGraphMLXmlDocument != null);
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            XmlDocument         oXmlDocument;
            XmlNamespaceManager oXmlNamespaceManager;

            // Some of the statistics are available in the user's profile.

            String sUrl = "http://gdata.youtube.com/feeds/api/users/"
                          + EncodeUrlParameter(sUserName);

            if (TryGetXmlDocument(sUrl, oRequestStatistics, out oXmlDocument,
                                  out oXmlNamespaceManager))
            {
                AppendYouTubeDateGraphMLAttributeValue(oXmlDocument,
                                                       "a:entry/a:published/text()", oXmlNamespaceManager,
                                                       oGraphMLXmlDocument, oVertexXmlNode, JoinedDateUtcID);

                AppendStringGraphMLAttributeValue(oXmlDocument,
                                                  "a:entry/media:thumbnail/@url", oXmlNamespaceManager,
                                                  oGraphMLXmlDocument, oVertexXmlNode,
                                                  NodeXLGraphMLUtil.VertexImageFileID);

                XmlNode oStatisticsXmlNode;

                if (XmlUtil2.TrySelectSingleNode(oXmlDocument,
                                                 "a:entry/yt:statistics", oXmlNamespaceManager,
                                                 out oStatisticsXmlNode))
                {
                    AppendInt32GraphMLAttributeValue(oStatisticsXmlNode,
                                                     "@videoWatchCount", oXmlNamespaceManager,
                                                     oGraphMLXmlDocument, oVertexXmlNode, VideosWatchedID);

                    AppendInt32GraphMLAttributeValue(oStatisticsXmlNode,
                                                     "@subscriberCount", oXmlNamespaceManager,
                                                     oGraphMLXmlDocument, oVertexXmlNode, SubscribersID);
                }
            }

            // The remaining statistics must be obtained from the totalResults
            // XML node within documents obtained from other URLs.

            const String TotalResultsXPath =
                "a:feed/openSearch:totalResults/text()";

            sUrl = GetFriendsOrSubscriptionsUrl(sUserName, true)
                   + "?max-results=1";

            AppendInt32GraphMLAttributeValue(sUrl, TotalResultsXPath,
                                             oGraphMLXmlDocument, oRequestStatistics, oVertexXmlNode,
                                             FriendsID);

            sUrl = GetFriendsOrSubscriptionsUrl(sUserName, false)
                   + "?max-results=1";

            AppendInt32GraphMLAttributeValue(sUrl, TotalResultsXPath,
                                             oGraphMLXmlDocument, oRequestStatistics, oVertexXmlNode,
                                             SubscriptionsID);

            sUrl = "http://gdata.youtube.com/feeds/api/users/"
                   + EncodeUrlParameter(sUserName) + "/uploads?max-results=1";

            AppendInt32GraphMLAttributeValue(sUrl, TotalResultsXPath,
                                             oGraphMLXmlDocument, oRequestStatistics, oVertexXmlNode,
                                             VideosUploadedID);
        }
        GetUserNetworkRecursive
        (
            String sUserName,
            WhatToInclude eWhatToInclude,
            Boolean bIncludeFriendsThisCall,
            NetworkLevel eNetworkLevel,
            Int32 iMaximumPeoplePerRequest,
            Int32 iRecursionLevel,
            GraphMLXmlDocument oGraphMLXmlDocument,
            Dictionary <String, XmlNode> oUserNameDictionary,
            RequestStatistics oRequestStatistics
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sUserName));

            Debug.Assert(eNetworkLevel == NetworkLevel.One ||
                         eNetworkLevel == NetworkLevel.OnePointFive ||
                         eNetworkLevel == NetworkLevel.Two);

            Debug.Assert(iMaximumPeoplePerRequest > 0);
            Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2);
            Debug.Assert(oGraphMLXmlDocument != null);
            Debug.Assert(oUserNameDictionary != null);
            Debug.Assert(oRequestStatistics != null);
            AssertValid();

            /*
             * Here is what this method should do, based on the eNetworkLevel and
             * iRecursionLevel parameters.
             *
             *      eNetworkLevel
             *
             |One               | OnePointFive      | Two
             *  ---|------------------| ------------------| -----------------
             * i   1  |Add all vertices. | Add all vertices. | Add all vertices.
             * R      |                  |                   |
             * e      |Add all edges.    | Add all edges.    | Add all edges.
             * c      |                  |                   |
             * u      |Do not recurse.   | Recurse.          | Recurse.
             * r      |                  |                   |
             * s   ---|------------------|-------------------|------------------
             * i   2  |Impossible.       | Do not add        | Add all vertices.
             * o      |                  | vertices.         |
             * n      |                  |                   |
             * L      |                  | Add edges only if | Add all edges.
             * e      |                  | vertices are      |
             * v      |                  | already included. |
             * e      |                  |                   |
             * l      |                  | Do not recurse.   | Do not recurse.
             |                  |                   |
             |  ---|------------------|-------------------|------------------
             */

            Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel,
                                                      iRecursionLevel);

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

            ReportProgressForFriendsOrSubscriptions(sUserName,
                                                    bIncludeFriendsThisCall);

            Boolean bThisUserAppended = false;

            // If the GraphMLXmlDocument already contains at least one vertex node,
            // then this is the second time that this method has been called, a
            // partial network has already been obtained, and most errors should
            // now be skipped.  However, if none of the network has been obtained
            // yet, errors on page 1 should throw an immediate exception.

            Boolean bSkipMostPage1Errors = oGraphMLXmlDocument.HasVertexXmlNode;

            // The document consists of a single "feed" node with zero or more
            // "entry" child nodes.

            foreach (XmlNode oEntryXmlNode in EnumerateXmlNodes(
                         GetFriendsOrSubscriptionsUrl(sUserName, bIncludeFriendsThisCall),
                         "a:feed/a:entry", iMaximumPeoplePerRequest, bSkipMostPage1Errors,
                         oRequestStatistics))
            {
                XmlNamespaceManager oXmlNamespaceManager =
                    CreateXmlNamespaceManager(oEntryXmlNode.OwnerDocument);

                String sOtherUserName;

                if (!XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode,
                                                          "yt:username/text()", oXmlNamespaceManager,
                                                          out sOtherUserName))
                {
                    continue;
                }

                if (!bThisUserAppended)
                {
                    // Append a vertex node for this request's user.
                    //
                    // This used to be done after the foreach loop, which avoided
                    // the need for a "bThisUserAppended" flag.  That caused the
                    // following bug: If a YouTube error occurred within
                    // EnumerateXmlNodes() after some edges had been added, and the
                    // user decided to import the resulting partial network, the
                    // GraphML might contain edges that referenced "this user"
                    // without containing a vertex for "this user."  That is an
                    // illegal state for GraphML, which the ExcelTemplate project
                    // caught and reported as an error.

                    TryAppendVertexXmlNode(sUserName, null, oGraphMLXmlDocument,
                                           oUserNameDictionary);

                    bThisUserAppended = true;
                }

                Boolean bNeedToAppendVertices = GetNeedToAppendVertices(
                    eNetworkLevel, iRecursionLevel);

                if (bNeedToAppendVertices)
                {
                    if (
                        TryAppendVertexXmlNode(sOtherUserName, oEntryXmlNode,
                                               oGraphMLXmlDocument, oUserNameDictionary)
                        &&
                        bNeedToRecurse
                        )
                    {
                        oUserNamesToRecurse.Add(sOtherUserName);
                    }
                }

                if (bNeedToAppendVertices ||
                    oUserNameDictionary.ContainsKey(sOtherUserName))
                {
                    String sRelationship;

                    if (bIncludeFriendsThisCall)
                    {
                        sRelationship = "Friend of";
                    }
                    else
                    {
                        sRelationship = "Subscribes to";
                        String sSubscriptionType = null;

                        if (XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode,

                                                                 "a:category[@scheme='http://gdata.youtube.com/schemas/"
                                                                 + "2007/subscriptiontypes.cat']/@term",

                                                                 oXmlNamespaceManager, out sSubscriptionType))
                        {
                            sRelationship += " " + sSubscriptionType;
                        }
                    }

                    NodeXLGraphMLUtil.AppendEdgeXmlNode(oGraphMLXmlDocument,
                                                        sUserName, sOtherUserName, sRelationship);
                }
            }

            if (bNeedToRecurse)
            {
                foreach (String sUserNameToRecurse in oUserNamesToRecurse)
                {
                    GetUserNetworkRecursive(sUserNameToRecurse,
                                            eWhatToInclude, bIncludeFriendsThisCall, eNetworkLevel,
                                            iMaximumPeoplePerRequest, 2, oGraphMLXmlDocument,
                                            oUserNameDictionary, oRequestStatistics);
                }
            }
        }