/// <summary>
        /// Given the full path to a project, return the value of the &lt;LinkedServerProject&gt; property,
        /// expanded to a full path.
        /// </summary>
        /// <param name="projectPath">The full path to the project possibly containing this property.</param>
        /// <returns>The full path of the RIA link.  It may be null or empty.</returns>
        private string LoadLinkFromProject(string projectPath)
        {
            var linkedServerProject = _projectFileReader.GetPropertyValue(projectPath, LinkedServerProjectPropertyName);

            // RIA Links are usually relative -- convert to full
            if (!string.IsNullOrEmpty(linkedServerProject))
            {
                linkedServerProject = ProjectFileReader.ConvertToFullPath(linkedServerProject, projectPath);

                // Emit a message to help user see we found a SimpleEntity Link
                _logger.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.SimpleEntity_Link_Present, projectPath, linkedServerProject));
            }

            return(linkedServerProject);
        }
        /// <summary>
        /// Loads the internal state from the breadcrumb file passed to the ctor.
        /// </summary>
        /// <remarks>
        /// If the root project has been modified since the breadcrumb file
        /// was written, the entire cache is considered invalid and <c>false</c> is returned.
        /// If any cached project has been touched since the cache was last written, just
        /// is portion of the cache will be reloaded from the project file.
        /// </remarks>
        /// <returns>A <c>true</c> means the cache was loaded from the breadcrumb file successfully.
        /// If we detect the cache is out of date or does not exist, a <c>false</c> is returned.
        /// </returns>
        internal bool LoadCacheFromFile()
        {
            this.IsFileCacheCurrent = false;

            if (!File.Exists(this._historyFilePath))
            {
                return(false);
            }

            // If the root project itself has been touched since the
            // time we wrote the file, we can't trust anything in our cache
            DateTime projectWriteTime    = File.GetLastWriteTime(this._rootProjectPath);
            DateTime breadCrumbWriteTime = File.GetLastWriteTime(this._historyFilePath);

            if (projectWriteTime.CompareTo(breadCrumbWriteTime) > 0)
            {
                return(false);
            }

            // Read the breadcrumb file.
            // Format is:
            //  One line per project: path, lastWriteTime, list of source files separated by commas
            string fileContents = File.ReadAllText(this._historyFilePath);

            string[] projectEntries = fileContents.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string projectEntry in projectEntries)
            {
                string[] projectItems = projectEntry.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                // Fewer than 2 is formatting problem -- maybe the file is corrupt.  Just discard cache and rebuild.
                if (projectItems.Length < 2)
                {
                    // Always clear out any partial results when returning false
                    this.SourceFilesByProject.Clear();
                    return(false);
                }
                string projectPath = projectItems[0];

                // If that project no longer exists, remove it from the cache but keep running
                if (!File.Exists(projectPath))
                {
                    continue;
                }

                List <string> files = new List <string>();

                // Projects with no source files have only the first 2 items (name and timestamp).
                // Those will be added to our cache with an empty list to show that we know they
                // have no source files (otherwise, we would need to open them again to know).
                // Any project with some number of source files falls into the body of this 'if'
                if (projectItems.Length >= 3)
                {
                    // Check whether the project file was touched since the last time
                    // we analyzed it.  Failure to parse last write time or discovery
                    // the project has been touched more recently causes us to reload
                    // just that project.  Incidentally, the use of Ticks here is more
                    // reliable than DateTime.ToString() which does not round-trip accurately.
                    projectWriteTime = File.GetLastWriteTime(projectPath);
                    long breadCrumbWriteTimeTicks = 0;
                    if (!long.TryParse(projectItems[1], out breadCrumbWriteTimeTicks) || projectWriteTime.Ticks > breadCrumbWriteTimeTicks)
                    {
                        // Go load from the project file and ignore what we had cached
                        files.AddRange(this._projectFileReader.LoadSourceFilesFromProject(projectPath));
                    }
                    else
                    {
                        // The last write time shows the project has not changed since
                        // we cached the results, so extract them from the text
                        for (int i = 2; i < projectItems.Length; ++i)
                        {
                            string file = projectItems[i];
                            if (string.IsNullOrEmpty(file))
                            {
                                continue;
                            }

                            // We write relative paths, so convert back to full
                            string fullFilePath = ProjectFileReader.ConvertToFullPath(file, projectPath);

                            // If the file has ceased to exist, but the project says it
                            // does, we do not add it to our internal lists
                            if (File.Exists(fullFilePath))
                            {
                                files.Add(fullFilePath);
                            }
                        }
                    }
                }
                this[projectPath] = files;
            }
            this.IsFileCacheCurrent = true;
            return(true);
        }