using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.XR.CoreUtils.Editor;
using UnityEngine;
namespace UnityEditor.XR.Interaction.Toolkit.ProjectValidation
{
///
/// Utility class to help with project validation for XR Interaction Toolkit and Samples.
///
internal static class ProjectValidationUtility
{
const string k_SamplesRootDirectoryName = "Samples";
///
/// This is the minimum version of the Starter Assets sample for the XR Interaction Toolkit that enforces correct behavior
/// of newly added properties, prefabs, or input actions.
///
public static readonly PackageVersion minimumXRIStarterAssetsSampleVersion = new PackageVersion("3.1.0");
///
/// Dictionary used to cache packages imported samples. The dictionary key is the package display name as displayed in package Samples directory.
///
static Dictionary s_PackageSampleCache;
struct PackageSampleData
{
public string packageDisplayName;
public Dictionary importedSamples;
}
///
/// Struct containing data about a sample.
///
struct SampleData
{
///
/// The display name of the sample, matches the directory.
///
public string sampleName;
///
/// The display name of the package the sample is from.
///
public string packageDisplayName;
///
/// The version of the package the sample is imported from.
///
public PackageVersion packageVersion;
}
///
/// Searches for a sample, from a specified package, .
/// The search is done by iterating through the Samples directory, but will utilize cached data if available.
/// This function is version agnostic and will return true if the sample is found in any version of the package's
/// sample directory.
///
/// The name of the package directory that contains the sample.
/// The name of the sample directory to search for.
/// Returns if the sample is found in the Samples directory of the specified
/// package. Otherwise, returns .
///
public static bool HasSampleImported(string packageDisplayName, string sampleDisplayName)
{
if (s_PackageSampleCache == null)
{
UpdatePackageSampleCache();
if (s_PackageSampleCache == null)
return false;
}
return s_PackageSampleCache.TryGetValue(packageDisplayName, out var sampleData) && sampleData.importedSamples.ContainsKey(sampleDisplayName);
}
///
/// Searches for the imported samples from a specified package.
///
/// The display name of the package (directory that contains the samples).
/// The list to populate with the unordered collection of imported samples.
public static void GetImportedSamples(string packageDisplayName, List<(string sampleName, PackageVersion packageVersion)> results)
{
results.Clear();
if (s_PackageSampleCache == null)
{
UpdatePackageSampleCache();
if (s_PackageSampleCache == null)
return;
}
if (s_PackageSampleCache.TryGetValue(packageDisplayName, out var sampleData))
{
foreach (var importedSample in sampleData.importedSamples)
{
results.Add((importedSample.Value.sampleName, importedSample.Value.packageVersion));
}
}
}
///
/// Searches for a sample, from a specified package,
/// and compares the sample version.
///
/// The name of the package directory that contains the sample.
/// The name of the sample directory to search for.
/// The minimum package version the sample should be imported from.
/// Returns if the sample is found in the Samples directory of the specified
/// package and the imported sample's version is greater than or equal to the .
public static bool SampleImportMeetsMinimumVersion(string packageDisplayName, string sampleDisplayName, PackageVersion minVersion)
{
if (HasSampleImported(packageDisplayName, sampleDisplayName))
return s_PackageSampleCache[packageDisplayName].importedSamples[sampleDisplayName].packageVersion >= minVersion;
return false;
}
///
/// Iterates through Samples directory and caches sample and package data.
///
static void UpdatePackageSampleCache()
{
if (s_PackageSampleCache == null)
{
try
{
s_PackageSampleCache = new Dictionary();
var delimiter = Path.DirectorySeparatorChar;
var sampleRootPath = Path.Combine(Path.GetFileName(Application.dataPath), k_SamplesRootDirectoryName);
if (!Directory.Exists(sampleRootPath))
{
Debug.LogWarning($"Could not find Samples directory ({sampleRootPath}). Failed to update package sample cache.");
return;
}
// Iterate through all package directories in Samples directory
var allSamplePackagesPaths = Directory.GetDirectories(sampleRootPath);
foreach (var packageDirectoryPath in allSamplePackagesPaths)
{
var packageDisplayName = packageDirectoryPath.Split(delimiter).Last();
// To contain all samples for this package
var sampleMap = new Dictionary();
// Iterate through all version directories in package
var versionDirectoryPaths = Directory.GetDirectories(packageDirectoryPath);
foreach (var versionDirectoryPath in versionDirectoryPaths)
{
var versionName = versionDirectoryPath.Split(delimiter).Last();
var packageVersion = new PackageVersion(versionName);
// Iterate through all sample directories in version
var samplesDirectoryPaths = Directory.GetDirectories(versionDirectoryPath);
foreach (var sampleDirectoryPath in samplesDirectoryPaths)
{
var sampleName = sampleDirectoryPath.Split(delimiter).Last();
sampleMap.Add(sampleName, new SampleData
{
sampleName = sampleName,
packageDisplayName = packageDisplayName,
packageVersion = packageVersion,
});
}
}
var packageCacheData = new PackageSampleData { packageDisplayName = packageDisplayName, importedSamples = sampleMap, };
s_PackageSampleCache[packageDisplayName] = packageCacheData;
}
}
catch (Exception e)
{
Debug.LogError("Failed to update package sample cache. " + e.Message);
}
}
}
}
}