public async Task <IEnumerable <Tuple <Identifier, Version, string> > > DoFetch(Identifier id, VersionRange range, LogHandler logCallback, ProgressHandler progressCallback) { // TODO: Throw exception on circular reference var resolveQueue = new Queue <Tuple <Identifier, VersionRange, string> >(); var revInstallSequence = new List <Tuple <Identifier, Version, string> >(); resolveQueue.Enqueue(new Tuple <Identifier, VersionRange, string>(id, range, "")); while (resolveQueue.Count > 0) { var elem = resolveQueue.Dequeue(); RemotePackageInfo remotePack = null; if (!elem.Item1.HasGuid || !elem.Item1.HasName) { // Get the full identifier first, so that installed package can be queried better logCallback(LogLevel.Info, Translation.Translate("bpmcore_context_downloadmetadata", elem.Item1)); remotePack = await CachedQueryRemotePackage(elem.Item1); if (remotePack == null) { if (string.IsNullOrEmpty(elem.Item3)) { throw new PackageNotFoundException(this, elem.Item1); } else { throw new DependencyException(this, elem.Item1.ToString(), elem.Item3, new PackageNotFoundException(this, elem.Item1)); } } else { var newIdentifier = elem.Item1; if (!newIdentifier.HasGuid && remotePack.ID.HasGuid) { newIdentifier.Guid = remotePack.ID.Guid; newIdentifier.HasGuid = true; } if (!newIdentifier.HasName && remotePack.ID.HasName) { newIdentifier.Name = remotePack.ID.Name; newIdentifier.HasName = true; } elem = new Tuple <Identifier, VersionRange, string>(newIdentifier, elem.Item2, elem.Item3); } } var installedPackInfo = LocalRegistry.QueryInstalledPackage(this, elem.Item1); // Skip if the dependency is already installed and the version satisfies the requirement if (installedPackInfo != null && elem.Item2.ContainsVersion(installedPackInfo.Version)) { logCallback(LogLevel.Debug, Translation.Translate("bpmcore_context_alreadyinstalled", elem.Item1.ToString(), installedPackInfo.Version)); continue; } // Get the remote metadata if (remotePack == null) { logCallback(LogLevel.Info, Translation.Translate("bpmcore_context_downloadmetadata", elem.Item1)); remotePack = await CachedQueryRemotePackage(elem.Item1); if (remotePack == null) { if (string.IsNullOrEmpty(elem.Item3)) { throw new PackageNotFoundException(this, elem.Item1); } else { throw new DependencyException(this, elem.Item1.ToString(), elem.Item3, new PackageNotFoundException(this, elem.Item1)); } } } // Check other dependncies for a highest suitable version var installable = DependencyHelper.GetSuitableVersion(this, remotePack, elem.Item3, elem.Item2); logCallback(LogLevel.Info, Translation.Translate("bpmcore_context_willinstall", elem.Item1, installable.Item1)); if (installable.Item1 < remotePack.AvailableVersions.Keys.Last()) { logCallback(LogLevel.Warning, Translation.Translate("bpmcore_context_cannotlatest", remotePack.AvailableVersions.Keys.Last(), remotePack.ID)); } // Download the file from internet logCallback(LogLevel.Info, Translation.Translate("bpmcore_context_downloading", installable.Item2)); var tempFile = await DownloadManager.AcquirePackage(this, elem.Item1, installable.Item1, installable.Item2, progressCallback); // Push further dependencies into the queue var packInfo = LocalRegistry.QueryExternalPackage(this, tempFile); logCallback(LogLevel.Debug, Translation.Translate("bpmcore_context_requires", packInfo.ID, string.Join(",", packInfo.Dependencies.Select(t => t.Key.ToString() + t.Value)))); foreach (var dependency in packInfo.Dependencies) { resolveQueue.Enqueue(new Tuple <Identifier, VersionRange, string>(dependency.Key, dependency.Value, packInfo.PlainName)); } if (revInstallSequence.Any(t => t.Item3 == tempFile)) { revInstallSequence.Remove(revInstallSequence.Single(t => t.Item3 == tempFile)); } revInstallSequence.Add(new Tuple <Identifier, Version, string>(remotePack.ID, installable.Item1, tempFile)); } revInstallSequence.Reverse(); return(revInstallSequence); }