/// <summary>
        /// This is overridden to allow use of an SQL backed MSDN content ID cache
        /// </summary>
        /// <param name="configuration">The component configuration</param>
        /// <returns>An MSDN resolver instance</returns>
        protected override MsdnResolver CreateMsdnResolver(XPathNavigator configuration)
        {
            MsdnResolver resolver;
            IDictionary<string, string> cache = null;
            int localCacheSize;

            if(BuildComponentCore.Data.ContainsKey(SharedMsdnContentIdCacheId))
                cache = BuildComponentCore.Data[SharedMsdnContentIdCacheId] as IDictionary<string, string>;

            // If the shared cache already exists, return an instance that uses it.  It is assumed that all
            // subsequent instances will use the same cache.
            if(cache != null)
                return new MsdnResolver(cache, true);

            XPathNavigator node = configuration.SelectSingleNode("msdnContentIdCache");

            // If a <cache> element is not specified, use the default resolver
            if(node == null)
                resolver = base.CreateMsdnResolver(configuration);
            else
            {
                node = configuration.SelectSingleNode("sqlCache");
                string connectionString = node.GetAttribute("connectionString", String.Empty);

                // If a connection string is not defined, use the default resolver
                if(String.IsNullOrWhiteSpace(connectionString))
                    resolver = base.CreateMsdnResolver(configuration);
                else
                {
                    string cacheSize = node.GetAttribute("msdnLocalCacheSize", String.Empty);

                    if(String.IsNullOrWhiteSpace(cacheSize) || !Int32.TryParse(cacheSize, out localCacheSize))
                        localCacheSize = 2500;

                    // Load or create the cache database and the resolver.  The resolver will dispose of the
                    // dictionary when it is disposed of since it implements IDisposable.
                    resolver = new MsdnResolver(new SqlDictionary<string>(connectionString, "ContentIds",
                        "TargetKey", "ContentId") { LocalCacheSize = localCacheSize }, false);

                    int cacheCount = resolver.MsdnContentIdCache.Count;

                    if(cacheCount == 0)
                    {
                        // Log a diagnostic message since looking up all IDs can significantly slow the build
                        base.WriteMessage(MessageLevel.Diagnostic, "The SQL MSDN content ID cache in '" +
                            connectionString + "' does not exist yet.  All IDs will be looked up in this " +
                            "build which will slow it down.");
                    }
                    else
                        base.WriteMessage(MessageLevel.Info, "{0} cached MSDN content ID entries exist", cacheCount);

                    BuildComponentCore.Data[SharedMsdnContentIdCacheId] = resolver.MsdnContentIdCache;
                }
            }

            return resolver;
        }
Ejemplo n.º 2
0
        //=====================================================================

        /// <summary>
        /// This is used to create an MSDN resolver for the reference link to use in looking up MSDN content iDs
        /// </summary>
        /// <param name="configuration">The component configuration</param>
        /// <returns>An MSDN resolver instance</returns>
        /// <remarks>This can be overridden in derived classes to provide persistent caches with backing stores
        /// other than the default dictionary serialized to a binary file.  It also allows sharing the cache
        /// across instances by placing it in the <see cref="BuildComponentCore.Data"/> dictionary using the key
        /// name <c>SharedMsdnContentIdCacheID</c>.
        ///
        /// <para>If overridden, the <see cref="UpdateMsdnContentIdCache"/> method should also be overridden to
        /// persist changes to the cache if needed.</para></remarks>
        protected virtual MsdnResolver CreateMsdnResolver(XPathNavigator configuration)
        {
            MsdnResolver newResolver;
            IDictionary <string, string> cache = null;

            if (BuildComponentCore.Data.ContainsKey(SharedMsdnContentIdCacheId))
            {
                cache = BuildComponentCore.Data[SharedMsdnContentIdCacheId] as IDictionary <string, string>;
            }

            // If the shared cache already exists, return an instance that uses it.  It is assumed that all
            // subsequent instances will use the same cache.
            if (cache != null)
            {
                return(new MsdnResolver(cache, true));
            }

            // If a <cache> element is not specified, we'll use the standard resolver without a persistent cache.
            // We will share it across all instances though.
            XPathNavigator node = configuration.SelectSingleNode("msdnContentIdCache");

            if (node == null)
            {
                newResolver = new MsdnResolver();
            }
            else
            {
                // Keep the filename.  If we own it, we'll update the cache file when disposed.
                msdnIdCacheFile = node.GetAttribute("path", String.Empty);

                if (String.IsNullOrWhiteSpace(msdnIdCacheFile))
                {
                    this.WriteMessage(MessageLevel.Error, "You must specify a path attribute value on the " +
                                      "msdnContentIdCache element.");
                }

                // Create the folder if it doesn't exist
                msdnIdCacheFile = Path.GetFullPath(Environment.ExpandEnvironmentVariables(msdnIdCacheFile));
                string path = Path.GetDirectoryName(msdnIdCacheFile);

                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

                // Load the cache if it exists
                if (!File.Exists(msdnIdCacheFile))
                {
                    // Logged as a diagnostic message since looking up all IDs can significantly slow the build
                    this.WriteMessage(MessageLevel.Diagnostic, "The MSDN content ID cache '" + msdnIdCacheFile +
                                      "' does not exist yet.  All IDs will be looked up in this build which will slow it down.");

                    newResolver = new MsdnResolver();
                }
                else
                {
                    using (FileStream fs = new FileStream(msdnIdCacheFile, FileMode.Open, FileAccess.Read,
                                                          FileShare.Read))
                    {
                        BinaryFormatter bf = new BinaryFormatter();
                        newResolver = new MsdnResolver((IDictionary <string, string>)bf.Deserialize(fs), false);

                        this.WriteMessage(MessageLevel.Info, "Loaded {0} cached MSDN content ID entries",
                                          newResolver.MsdnContentIdCache.Count);
                    }
                }
            }

            BuildComponentCore.Data[SharedMsdnContentIdCacheId] = newResolver.MsdnContentIdCache;

            return(newResolver);
        }
Ejemplo n.º 3
0
        //=====================================================================

        /// <inheritdoc />
        public override void Initialize(XPathNavigator configuration)
        {
            TargetDictionary  newTargets;
            ReferenceLinkType type;
            string            attrValue, id;

            targets  = new TargetTypeDictionary();
            resolver = new LinkTextResolver(targets);

            // Get the shared instances dictionary.  Create it if it doesn't exist.
            if (BuildComponentCore.Data.ContainsKey(SharedReferenceTargetsId))
            {
                sharedTargets = BuildComponentCore.Data[SharedReferenceTargetsId] as Dictionary <string, TargetDictionary>;
            }

            if (sharedTargets == null)
            {
                BuildComponentCore.Data[SharedReferenceTargetsId] = sharedTargets = new Dictionary <string, TargetDictionary>();
            }

            // base-url is an xpath expression applied against the current document to pick up the save location of the
            // document. If specified, local links will be made relative to the base-url.
            string baseUrlValue = configuration.GetAttribute("base-url", String.Empty);

            if (!String.IsNullOrEmpty(baseUrlValue))
            {
                baseUrl = XPathExpression.Compile(baseUrlValue);
            }

            // hrefFormat is a string format that is used to format the value of local href attributes. The
            // default is "{0}.htm" if not specified.
            hrefFormat = (string)configuration.Evaluate("string(hrefFormat/@value)");

            if (String.IsNullOrWhiteSpace(hrefFormat))
            {
                hrefFormat = "{0}.htm";
            }

            // The container XPath can be replaced; this is useful
            string containerValue = configuration.GetAttribute("container", String.Empty);

            if (!String.IsNullOrEmpty(containerValue))
            {
                XmlTargetDictionaryUtilities.ContainerExpression = containerValue;
            }

            XPathNodeIterator targetsNodes = configuration.Select("targets");

#if DEBUG
            this.WriteMessage(MessageLevel.Diagnostic, "Loading reference link target info");

            DateTime startLoad = DateTime.Now;
#endif
            foreach (XPathNavigator targetsNode in targetsNodes)
            {
                // Get target type
                attrValue = targetsNode.GetAttribute("type", String.Empty);

                if (String.IsNullOrEmpty(attrValue))
                {
                    this.WriteMessage(MessageLevel.Error, "Each targets element must have a type attribute " +
                                      "that specifies which type of links to create");
                }

                if (!Enum.TryParse <ReferenceLinkType>(attrValue, true, out type))
                {
                    this.WriteMessage(MessageLevel.Error, "'{0}' is not a supported reference link type",
                                      attrValue);
                }

                // Check for shared instance by ID.  If not there, create it and add it.
                id = targetsNode.GetAttribute("id", String.Empty);

                if (!sharedTargets.TryGetValue(id, out newTargets))
                {
                    this.WriteMessage(MessageLevel.Info, "Loading {0} reference link type targets", type);

                    newTargets = this.CreateTargetDictionary(targetsNode);
                    sharedTargets[newTargets.DictionaryId] = newTargets;
                }

                targets.Add(type, newTargets);
            }

#if DEBUG
            TimeSpan loadTime = (DateTime.Now - startLoad);
            this.WriteMessage(MessageLevel.Diagnostic, "Load time: {0} seconds", loadTime.TotalSeconds);

            // Dump targets for comparison to other versions
//            targets.DumpTargetDictionary(Path.GetFullPath("TargetDictionary.xml"));

            // Serialization test
//            targets.SerializeDictionary(Directory.GetCurrentDirectory());
#endif
            // Getting the count from a database cache can be expensive so only report it if it will be seen
            if (this.BuildAssembler.VerbosityLevel == MessageLevel.Info)
            {
                this.WriteMessage(MessageLevel.Info, "{0} total reference link targets", targets.Count);
            }

            if (targets.NeedsMsdnResolver)
            {
                this.WriteMessage(MessageLevel.Info, "Creating MSDN URL resolver");

                msdnResolver = this.CreateMsdnResolver(configuration);

                string localeValue = (string)configuration.Evaluate("string(locale/@value)");

                if (msdnResolver != null && !String.IsNullOrWhiteSpace(localeValue))
                {
                    msdnResolver.Locale = localeValue;
                }
            }

            linkTarget = (string)configuration.Evaluate("string(linkTarget/@value)");

            if (String.IsNullOrWhiteSpace(linkTarget))
            {
                linkTarget = "_blank";
            }
        }
        /// <summary>
        /// This is overridden to allow use of an ESENT backed MSDN content ID cache
        /// </summary>
        /// <param name="configuration">The component configuration</param>
        /// <returns>An MSDN resolver instance</returns>
        protected override MsdnResolver CreateMsdnResolver(XPathNavigator configuration)
        {
            MsdnResolver resolver;
            IDictionary<string, string> cache = null;
            int localCacheSize;

            if(BuildComponentCore.Data.ContainsKey(SharedMsdnContentIdCacheId))
                cache = BuildComponentCore.Data[SharedMsdnContentIdCacheId] as IDictionary<string, string>;

            // If the shared cache already exists, return an instance that uses it.  It is assumed that all
            // subsequent instances will use the same cache.
            if(cache != null)
                return new MsdnResolver(cache, true);

            XPathNavigator node = configuration.SelectSingleNode("msdnContentIdCache");

            // If an <msdnContentIdCache> element is not specified, use the default resolver
            if(node == null)
                resolver = base.CreateMsdnResolver(configuration);
            else
            {
                string msdnIdCachePath = node.GetAttribute("cachePath", String.Empty);

                // If a cache path is not defined, use the default resolver
                if(String.IsNullOrWhiteSpace(msdnIdCachePath))
                    resolver = base.CreateMsdnResolver(configuration);
                else
                {
                    msdnIdCachePath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(msdnIdCachePath));

                    string cacheSize = node.GetAttribute("localCacheSize", String.Empty);

                    if(String.IsNullOrWhiteSpace(cacheSize) || !Int32.TryParse(cacheSize, out localCacheSize))
                        localCacheSize = 2500;

                    // Load or create the cache database and the resolver.  The resolver will dispose of the
                    // dictionary when it is disposed of since it implements IDisposable.  We won't compress the
                    // columns as they're typically not that big and there aren't usually that many entries.
                    // This gives a slight performance increase.
                    resolver = new MsdnResolver(new PersistentDictionary<string, string>(msdnIdCachePath, false)
                        { LocalCacheSize = localCacheSize }, false);

                    // We own the cache and will report statistics when done
                    ownsResolverCache = true;

                    int cacheCount = resolver.MsdnContentIdCache.Count;

                    if(cacheCount == 0)
                    {
                        // Log a diagnostic message since looking up all IDs can significantly slow the build
                        base.WriteMessage(MessageLevel.Diagnostic, "The ESENT MSDN content ID cache in '" +
                            msdnIdCachePath + "' does not exist yet.  All IDs will be looked up in this build " +
                            "which will slow it down.");
                    }
                    else
                        base.WriteMessage(MessageLevel.Info, "{0} cached MSDN content ID entries exist", cacheCount);

                    BuildComponentCore.Data[SharedMsdnContentIdCacheId] = resolver.MsdnContentIdCache;
                }
            }

            return resolver;
        }
Ejemplo n.º 5
0
        //=====================================================================

        /// <summary>
        /// This is used to create an MSDN resolver for the reference link to use in looking up MSDN content iDs
        /// </summary>
        /// <param name="configuration">The component configuration</param>
        /// <returns>An MSDN resolver instance</returns>
        /// <remarks>This can be overridden in derived classes to provide persistent caches with backing stores
        /// other than the default dictionary serialized to a binary file.  It also allows sharing the cache
        /// across instances by placing it in the <see cref="BuildComponentCore.Data"/> dictionary using the key
        /// name <c>SharedMsdnContentIdCacheID</c>.
        /// 
        /// <para>If overridden, the <see cref="UpdateMsdnContentIdCache"/> method should also be overridden to
        /// persist changes to the cache if needed.</para></remarks>
        protected virtual MsdnResolver CreateMsdnResolver(XPathNavigator configuration)
        {
            MsdnResolver newResolver;
            IDictionary<string, string> cache = null;

            if(BuildComponentCore.Data.ContainsKey(SharedMsdnContentIdCacheId))
                cache = BuildComponentCore.Data[SharedMsdnContentIdCacheId] as IDictionary<string, string>;

            // If the shared cache already exists, return an instance that uses it.  It is assumed that all
            // subsequent instances will use the same cache.
            if(cache != null)
                return new MsdnResolver(cache, true);

            // If a <cache> element is not specified, we'll use the standard resolver without a persistent cache.
            // We will share it across all instances though.
            XPathNavigator node = configuration.SelectSingleNode("msdnContentIdCache");

            if(node == null)
                newResolver = new MsdnResolver();
            else
            {
                // Keep the filename.  If we own it, we'll update the cache file when disposed.
                msdnIdCacheFile = node.GetAttribute("path", String.Empty);

                if(String.IsNullOrWhiteSpace(msdnIdCacheFile))
                    this.WriteMessage(MessageLevel.Error, "You must specify a path attribute value on the " +
                        "msdnContentIdCache element.");

                // Create the folder if it doesn't exist
                msdnIdCacheFile = Path.GetFullPath(Environment.ExpandEnvironmentVariables(msdnIdCacheFile));
                string path = Path.GetDirectoryName(msdnIdCacheFile);

                if(!Directory.Exists(path))
                    Directory.CreateDirectory(path);

                // Load the cache if it exists
                if(!File.Exists(msdnIdCacheFile))
                {
                    // Logged as a diagnostic message since looking up all IDs can significantly slow the build
                    this.WriteMessage(MessageLevel.Diagnostic, "The MSDN content ID cache '" + msdnIdCacheFile +
                        "' does not exist yet.  All IDs will be looked up in this build which will slow it down.");

                    newResolver = new MsdnResolver();
                }
                else
                    using(FileStream fs = new FileStream(msdnIdCacheFile, FileMode.Open, FileAccess.Read,
                      FileShare.Read))
                    {
                        BinaryFormatter bf = new BinaryFormatter();
                        newResolver = new MsdnResolver((IDictionary<string, string>)bf.Deserialize(fs), false);

                        this.WriteMessage(MessageLevel.Info, "Loaded {0} cached MSDN content ID entries",
                            newResolver.MsdnContentIdCache.Count);
                    }
            }

            BuildComponentCore.Data[SharedMsdnContentIdCacheId] = newResolver.MsdnContentIdCache;

            return newResolver;
        }
Ejemplo n.º 6
0
        //=====================================================================

        /// <inheritdoc />
        public override void Initialize(XPathNavigator configuration)
        {
            TargetDictionary newTargets;
            ReferenceLinkType type;
            string attrValue, id;

            targets = new TargetTypeDictionary();
            resolver = new LinkTextResolver(targets);

            // Get the shared instances dictionary.  Create it if it doesn't exist.
            if(BuildComponentCore.Data.ContainsKey(SharedReferenceTargetsId))
                sharedTargets = BuildComponentCore.Data[SharedReferenceTargetsId] as Dictionary<string, TargetDictionary>;

            if(sharedTargets == null)
                BuildComponentCore.Data[SharedReferenceTargetsId] = sharedTargets = new Dictionary<string, TargetDictionary>();

            // base-url is an xpath expression applied against the current document to pick up the save location of the
            // document. If specified, local links will be made relative to the base-url.
            string baseUrlValue = configuration.GetAttribute("base-url", String.Empty);

            if(!String.IsNullOrEmpty(baseUrlValue))
                baseUrl = XPathExpression.Compile(baseUrlValue);

            // hrefFormat is a string format that is used to format the value of local href attributes. The
            // default is "{0}.htm" if not specified.
            hrefFormat = (string)configuration.Evaluate("string(hrefFormat/@value)");

            if(String.IsNullOrWhiteSpace(hrefFormat))
                hrefFormat = "{0}.htm";

            // The container XPath can be replaced; this is useful
            string containerValue = configuration.GetAttribute("container", String.Empty);

            if(!String.IsNullOrEmpty(containerValue))
                XmlTargetDictionaryUtilities.ContainerExpression = containerValue;

            XPathNodeIterator targetsNodes = configuration.Select("targets");

#if DEBUG
            this.WriteMessage(MessageLevel.Diagnostic, "Loading reference link target info");

            DateTime startLoad = DateTime.Now;
#endif
            foreach(XPathNavigator targetsNode in targetsNodes)
            {
                // Get target type
                attrValue = targetsNode.GetAttribute("type", String.Empty);

                if(String.IsNullOrEmpty(attrValue))
                    this.WriteMessage(MessageLevel.Error, "Each targets element must have a type attribute " +
                        "that specifies which type of links to create");

                if(!Enum.TryParse<ReferenceLinkType>(attrValue, true, out type))
                    this.WriteMessage(MessageLevel.Error, "'{0}' is not a supported reference link type",
                        attrValue);

                // Check for shared instance by ID.  If not there, create it and add it.
                id = targetsNode.GetAttribute("id", String.Empty);

                if(!sharedTargets.TryGetValue(id, out newTargets))
                {
                    this.WriteMessage(MessageLevel.Info, "Loading {0} reference link type targets", type);

                    newTargets = this.CreateTargetDictionary(targetsNode);
                    sharedTargets[newTargets.DictionaryId] = newTargets;
                }

                targets.Add(type, newTargets);
            }

#if DEBUG
            TimeSpan loadTime = (DateTime.Now - startLoad);
            this.WriteMessage(MessageLevel.Diagnostic, "Load time: {0} seconds", loadTime.TotalSeconds);

            // Dump targets for comparison to other versions
//            targets.DumpTargetDictionary(Path.GetFullPath("TargetDictionary.xml"));

            // Serialization test
//            targets.SerializeDictionary(Directory.GetCurrentDirectory());
#endif
            // Getting the count from a database cache can be expensive so only report it if it will be seen
            if(this.BuildAssembler.VerbosityLevel == MessageLevel.Info)
                this.WriteMessage(MessageLevel.Info, "{0} total reference link targets", targets.Count);

            if(targets.NeedsMsdnResolver)
            {
                this.WriteMessage(MessageLevel.Info, "Creating MSDN URL resolver");

                msdnResolver = this.CreateMsdnResolver(configuration);

                string localeValue = (string)configuration.Evaluate("string(locale/@value)");

                if(msdnResolver != null && !String.IsNullOrWhiteSpace(localeValue))
                    msdnResolver.Locale = localeValue;
            }

            linkTarget = (string)configuration.Evaluate("string(linkTarget/@value)");

            if(String.IsNullOrWhiteSpace(linkTarget))
                linkTarget = "_blank";
        }