protected async Task AddHtmlAttributeValueCompletionData (CompletionDataList list, HtmlSchema schema, 
		                                                          XName tagName, XName attributeName, CancellationToken token)
		{
			list.AddRange (await schema.CompletionProvider.GetAttributeValueCompletionData (tagName.FullName, 
			                                                                          attributeName.FullName, token));
		}
		protected static async Task AddHtmlTagCompletionData (CompletionDataList list, HtmlSchema schema, XName parentName, CancellationToken token)
		{
			if (schema == null)
				return;
			
			if (parentName.IsValid) {
				list.AddRange (await schema.CompletionProvider.GetChildElementCompletionData (parentName.FullName.ToLower (), token));
			} else {
				list.AddRange (await schema.CompletionProvider.GetElementCompletionData (token));
			}			
		}
		protected async Task AddHtmlAttributeCompletionData (CompletionDataList list, HtmlSchema schema, 
		                                                     XName tagName, Dictionary<string, string> existingAtts, CancellationToken token)
		{
			//add atts only if they're not aready in the tag
			foreach (var datum in await schema.CompletionProvider.GetAttributeCompletionData (tagName.FullName.ToLower (), token))
				if (existingAtts == null || !existingAtts.ContainsKey (datum.DisplayText))
					list.Add (datum);
		}
		protected void AddHtmlAttributeValueCompletionData (CompletionDataList list, HtmlSchema schema, 
		    XName tagName, XName attributeName)
		{
			list.AddRange (schema.CompletionProvider.GetAttributeValueCompletionData (tagName.FullName, 
			                                                                          attributeName.FullName));
		}
		protected static void AddHtmlTagCompletionData (CompletionDataList list, HtmlSchema schema, XName parentName)
		{
			if (schema == null)
				return;
			
			if (parentName.IsValid) {
				list.AddRange (schema.CompletionProvider.GetChildElementCompletionData (parentName.FullName.ToLower ()));
			} else {
				list.AddRange (schema.CompletionProvider.GetElementCompletionData ());
			}			
		}
		public static void Initialise ()
		{
			if (schemas != null)
				return;
			
			schemas = new Dictionary<string, HtmlSchema> ();
			
			//load all the schemas from addin points
			//NOTE: the first ([0]) schema must be the default schema
			
			foreach (DocTypeExtensionNode node in Mono.Addins.AddinManager.GetExtensionNodes ("/MonoDevelop/Html/DocTypes")) {
				if (schemas.ContainsKey (node.Name))
					LoggingService.LogWarning (
					    "HtmlSchemaService cannot register duplicate doctype with the name '{0}'", node.Name);
				
				if (!string.IsNullOrEmpty (node.XsdFile)) {
					string path = node.Addin.GetFilePath (node.XsdFile);
					try {
						IXmlCompletionProvider provider = new XmlSchemaCompletionData (path);
						schemas.Add (node.Name, new HtmlSchema (node.Name, node.FullName, provider));
					} catch (Exception ex) {
						LoggingService.LogWarning (
						    "HtmlSchemaService encountered an error registering the schema '" + path + "'", ex);
					}
				} else {
					schemas.Add (node.Name, new HtmlSchema (node.Name, node.FullName, node.CompletionDocTypeName));
				}
			}
			
			//initialise the default backup schema if it doesn't exist already
			if (!schemas.ContainsKey (DefaultDocTypeName)) {
				HtmlSchema defaultSubstProvider = schemas["XHTML 1.0 Transitional"];
				IXmlCompletionProvider provider;
				if (defaultSubstProvider != null) {
					//start the threaded schema loading
					LoadSchema (defaultSubstProvider, true);
					provider = defaultSubstProvider.CompletionProvider;
				} else {
					LoggingService.LogWarning ("Completion schema for default HTML doctype not found.");
					provider = new EmptyXmlCompletionProvider ();
				}
				
				schemas[DefaultDocTypeName] = new HtmlSchema ("HTML 4.01 Transitional",
				    "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
				    + "\"http://www.w3.org/TR/html4/loose.dtd\">",
				    provider);
			}
			
			MonoDevelop.Core.LoggingService.LogDebug ("HtmlSchemaService initialised");
		}
		public static IXmlCompletionProvider LazyGetProvider (HtmlSchema schema)
		{
			//get the provided schema, if it's loaded
			HtmlSchema hs = LoadSchema (schema, true);
			//fall back to the defaul provider
			if (hs == null)
				hs = LoadSchema (DefaultDocType, true);
			//fall back to a blank provider
			return new EmptyXmlCompletionProvider ();
		}
		//if lazy == true, returns null if the schema isn't loaded yet
		static HtmlSchema LoadSchema (HtmlSchema schema, bool lazy)
		{
			ILazilyLoadedProvider lazyProv = schema.CompletionProvider as ILazilyLoadedProvider;
			if (lazyProv == null || lazyProv.IsLoaded) {
				return schema;
			} else {
				//FIXME: actually implement threaded loading and return null if loading && lazy
				lazyProv.EnsureLoaded ();
				return schema;
			}
		}