private Indexable GetNewXmpIndexable(ref Indexable indexable,
                                             string path,
                                             Guid id,
                                             DirectoryModel parent)
        {
            // In non-crawl mode, check if a corresponding xmp file is present and not already scheduled and index it.
            // If file.xmp and file are rapidly written/updated (in that order), this does the right thing.
            // If file and file.xmp are rapidly written/updated (in that order), either
            // - file.xmp is present during FileToIndexable(file): in which case xmp properties are
            //   added to file; and when file.xmp is indexed, it will replace the xmp properties
            // - file.xmp is not present during FileToIndexable(file): when the xmp file is later indexed
            //   it will add the xmp properties
            // since the uid file will still be in the uid-cache, correct uid will be used for xmp prop-change indexable
            string possible_xmp_file_path = string.Concat(path, ".xmp");

            if (!File.Exists(possible_xmp_file_path))
            {
                return(null);
            }

            Guid xmp_id = queryable.RegisterFile(parent, (Path.GetFileName(possible_xmp_file_path)));

            if (xmp_id == Guid.Empty)
            {
                return(null);
            }

            XmpFile xmp_file = null;

            try {
                xmp_file = new XmpFile(possible_xmp_file_path);
            } catch {
                uid_manager.ForgetNewId(possible_xmp_file_path);
                return(null);
            }

            // FIXME: Should also delete previous xmp properties!
            foreach (Property p in xmp_file.Properties)
            {
                p.IsMutable = true;
                indexable.AddProperty(p);
            }
            xmp_file.Close();

            // Also need to save some local states for PostAddHook,
            // namely, path to the xmp file, path to basefile and generated uid
            indexable.LocalState ["XmpFilePath"]  = possible_xmp_file_path;
            indexable.LocalState ["BaseFilePath"] = path;
            indexable.LocalState ["XmpGuid"]      = GuidFu.ToShortString(xmp_id);
            if (Debug)
            {
                Log.Debug("Adding properties from {0}({2}) to {1}({3})", possible_xmp_file_path, path, GuidFu.ToShortString(xmp_id), GuidFu.ToShortString(id));
            }

            return(null);
        }
		private Indexable GetNewXmpIndexable (ref Indexable indexable,
						      string path,
					       	      Guid id,
					       	      DirectoryModel parent)
		{
			// In non-crawl mode, check if a corresponding xmp file is present and not already scheduled and index it.
			// If file.xmp and file are rapidly written/updated (in that order), this does the right thing.
			// If file and file.xmp are rapidly written/updated (in that order), either
			// - file.xmp is present during FileToIndexable(file): in which case xmp properties are
			//   added to file; and when file.xmp is indexed, it will replace the xmp properties
			// - file.xmp is not present during FileToIndexable(file): when the xmp file is later indexed
			//   it will add the xmp properties
			// since the uid file will still be in the uid-cache, correct uid will be used for xmp prop-change indexable
			string possible_xmp_file_path = string.Concat (path, ".xmp");
			if (! File.Exists (possible_xmp_file_path))
				return null;

			Guid xmp_id = queryable.RegisterFile (parent, (Path.GetFileName (possible_xmp_file_path)));
			if (xmp_id == Guid.Empty)
				return null;

			XmpFile xmp_file = null;
			try {
				xmp_file = new XmpFile (possible_xmp_file_path);
			} catch {
				uid_manager.ForgetNewId (possible_xmp_file_path);
				return null;
			}

			// FIXME: Should also delete previous xmp properties!
			foreach (Property p in xmp_file.Properties) {
				p.IsMutable = true;
				indexable.AddProperty (p);
			}
			xmp_file.Close ();

			// Also need to save some local states for PostAddHook,
			// namely, path to the xmp file, path to basefile and generated uid
			indexable.LocalState ["XmpFilePath"] = possible_xmp_file_path;
			indexable.LocalState ["BaseFilePath"] = path;
			indexable.LocalState ["XmpGuid"] = GuidFu.ToShortString (xmp_id);
			if (Debug)
				Log.Debug ("Adding properties from {0}({2}) to {1}({3})", possible_xmp_file_path, path, GuidFu.ToShortString (xmp_id), GuidFu.ToShortString (id));

			return null;
		}
		public Indexable GetXmpQueryable (string path, Guid id, DirectoryModel parent)
		{
			Log.Debug ("Asked to create xmp indexable for ({0}) {1}", GuidFu.ToShortString (id), path);
			// Should be at least 6 characters /<...>.xmp
			if (path.Length < 6)
				return null;

			string basefile_path = Path.ChangeExtension (path, null);
			// Ignore xmp files by itself
			// FIXME: To support indexing independent xmp files will require even greater trouble
			if (! File.Exists (basefile_path))
				return null;

			XmpFile xmp_file = null;
			try {
				xmp_file = new XmpFile (path);
			} catch {
				Log.Warn ("Cannot create xmpfile from {0}", path);
				return null;
			}

			// Try to get the correct uid for the basefile
			// First we need to see if basefile is already scheduled (yet to be dispatched)
			Uri basefile_uri = null;
			Indexable base_indexable;

			if (uid_manager.HasNewId (basefile_path)) {
				// Since uid_manager has a new id for this basefile, so basefile is already scheduled
				// Get basefile uid from there
				Guid basefile_id = uid_manager.GetNewId (basefile_path);
				basefile_uri = GuidFu.ToUri (basefile_id);
				Log.Debug ("{0} is already scheduled with uri {1}", basefile_path, basefile_uri);
			} else {
				// Basefile is not scheduled in the current batch
				string basefile_name = Path.GetFileName (basefile_path);
				// Try to schedule it for addition
				base_indexable = queryable.GetCrawlingFileIndexable (parent, basefile_name);

				if (base_indexable == null) {
					// GetCrawlingFileIndexable returns null if file does not need to be indexed
					// So basefile is up-to-date
					// Need to figure out id from uid manager
					Guid basefile_id = uid_manager.GetIdByNameAndParentId (basefile_name, parent.UniqueId);
					basefile_uri = GuidFu.ToUri (basefile_id);
					Log.Debug ("{0} is not scheduled and need not be, uri is {1}", basefile_path, basefile_uri);
				} else {
					Log.Debug ("Need to index {0}", basefile_path);
					// basefile needs to be indexed
					// FIXME: Move the task business out of handler and into FSQ.cs
					Scheduler.Task task;
					task = queryable.NewAddTask (base_indexable);
					// FIXME: What is the correct priority ?
					// If should have similar priority to the one that this xmp-indexable will be a part of
					task.Priority = Scheduler.Priority.Immediate;
					queryable.ThisScheduler.Add (task);

					// Get the basefile uri from the indexable
					basefile_uri = base_indexable.Uri;
				}
			}

			Log.Debug ("Adding xmp-indexable for {0} (basefile uri {1}) with uid {2}",
				path,
				basefile_uri,
				GuidFu.ToShortString (id));

			Indexable indexable = new Indexable (IndexableType.PropertyChange, basefile_uri);
			// Set the timestamp of the indexable as the timestamp of the basefile
			// It could have also been skipped, the original Indexable.Add would anyway have it
			indexable.Timestamp = File.GetLastWriteTimeUtc (basefile_path);
			indexable.DisplayUri = UriFu.PathToFileUri (path);

			// If the file was somehow deleted before this point, bail out.
			if (! FileSystem.ExistsByDateTime (indexable.Timestamp)) {
				xmp_file.Close ();
				return null;
			}

			// Save some local states for PostAddHook, namely, path to the xmp file, path to basefile and generated uid
			indexable.LocalState ["XmpFilePath"] = path;
			indexable.LocalState ["BaseFilePath"] = basefile_path;
			indexable.LocalState ["XmpGuid"] = GuidFu.ToShortString (id);

			// FIXME: Should also delete previous xmp properties!
			foreach (Property p in xmp_file.Properties) {
				p.IsMutable = true;
				indexable.AddProperty (p);
			}
			xmp_file.Close ();

			return indexable;
		}
        public Indexable GetXmpQueryable(string path, Guid id, DirectoryModel parent)
        {
            Log.Debug("Asked to create xmp indexable for ({0}) {1}", GuidFu.ToShortString(id), path);
            // Should be at least 6 characters /<...>.xmp
            if (path.Length < 6)
            {
                return(null);
            }

            string basefile_path = Path.ChangeExtension(path, null);

            // Ignore xmp files by itself
            // FIXME: To support indexing independent xmp files will require even greater trouble
            if (!File.Exists(basefile_path))
            {
                return(null);
            }

            XmpFile xmp_file = null;

            try {
                xmp_file = new XmpFile(path);
            } catch {
                Log.Warn("Cannot create xmpfile from {0}", path);
                return(null);
            }

            // Try to get the correct uid for the basefile
            // First we need to see if basefile is already scheduled (yet to be dispatched)
            Uri       basefile_uri = null;
            Indexable base_indexable;

            if (uid_manager.HasNewId(basefile_path))
            {
                // Since uid_manager has a new id for this basefile, so basefile is already scheduled
                // Get basefile uid from there
                Guid basefile_id = uid_manager.GetNewId(basefile_path);
                basefile_uri = GuidFu.ToUri(basefile_id);
                Log.Debug("{0} is already scheduled with uri {1}", basefile_path, basefile_uri);
            }
            else
            {
                // Basefile is not scheduled in the current batch
                string basefile_name = Path.GetFileName(basefile_path);
                // Try to schedule it for addition
                base_indexable = queryable.GetCrawlingFileIndexable(parent, basefile_name);

                if (base_indexable == null)
                {
                    // GetCrawlingFileIndexable returns null if file does not need to be indexed
                    // So basefile is up-to-date
                    // Need to figure out id from uid manager
                    Guid basefile_id = uid_manager.GetIdByNameAndParentId(basefile_name, parent.UniqueId);
                    basefile_uri = GuidFu.ToUri(basefile_id);
                    Log.Debug("{0} is not scheduled and need not be, uri is {1}", basefile_path, basefile_uri);
                }
                else
                {
                    Log.Debug("Need to index {0}", basefile_path);
                    // basefile needs to be indexed
                    // FIXME: Move the task business out of handler and into FSQ.cs
                    Scheduler.Task task;
                    task = queryable.NewAddTask(base_indexable);
                    // FIXME: What is the correct priority ?
                    // If should have similar priority to the one that this xmp-indexable will be a part of
                    task.Priority = Scheduler.Priority.Immediate;
                    queryable.ThisScheduler.Add(task);

                    // Get the basefile uri from the indexable
                    basefile_uri = base_indexable.Uri;
                }
            }

            Log.Debug("Adding xmp-indexable for {0} (basefile uri {1}) with uid {2}",
                      path,
                      basefile_uri,
                      GuidFu.ToShortString(id));

            Indexable indexable = new Indexable(IndexableType.PropertyChange, basefile_uri);

            // Set the timestamp of the indexable as the timestamp of the basefile
            // It could have also been skipped, the original Indexable.Add would anyway have it
            indexable.Timestamp  = File.GetLastWriteTimeUtc(basefile_path);
            indexable.DisplayUri = UriFu.PathToFileUri(path);

            // If the file was somehow deleted before this point, bail out.
            if (!FileSystem.ExistsByDateTime(indexable.Timestamp))
            {
                xmp_file.Close();
                return(null);
            }

            // Save some local states for PostAddHook, namely, path to the xmp file, path to basefile and generated uid
            indexable.LocalState ["XmpFilePath"]  = path;
            indexable.LocalState ["BaseFilePath"] = basefile_path;
            indexable.LocalState ["XmpGuid"]      = GuidFu.ToShortString(id);

            // FIXME: Should also delete previous xmp properties!
            foreach (Property p in xmp_file.Properties)
            {
                p.IsMutable = true;
                indexable.AddProperty(p);
            }
            xmp_file.Close();

            return(indexable);
        }