using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UComponent = UnityEngine.Component;
using UObject = UnityEngine.Object;
#if DOTCRAFT_ATTACH
namespace DotCraft.Unity
#else
namespace DotCraft.Editor
#endif
{
///
/// Small helper surface for Unity Editor snippets executed through dotcraft-unity.
///
public static class Dcu
{
private const BindingFlags AnyInstance =
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags AnyStatic =
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
///
/// Finds a loaded type by assembly-qualified name, full name, or unique short name.
///
public static Type Type(string typeName, bool throwIfMissing = true)
{
if (string.IsNullOrWhiteSpace(typeName))
return throwIfMissing ? throw new InvalidOperationException("Type name is empty.") : null;
var trimmed = typeName.Trim();
var direct = System.Type.GetType(trimmed, false);
if (direct != null)
return direct;
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic))
{
Type exact;
try
{
exact = assembly.GetType(trimmed, false);
}
catch
{
continue;
}
if (exact != null)
return exact;
}
var matches = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic)
.SelectMany(GetLoadableTypes)
.Where(t => string.Equals(t.FullName, trimmed, StringComparison.Ordinal)
|| string.Equals(t.Name, trimmed, StringComparison.Ordinal))
.Distinct()
.OrderBy(t => t.FullName, StringComparer.Ordinal)
.Take(4)
.ToArray();
if (matches.Length == 1)
return matches[0];
if (matches.Length > 1)
{
throw new InvalidOperationException(
$"Type '{trimmed}' is ambiguous: {string.Join(", ", matches.Select(t => t.FullName))}");
}
if (throwIfMissing)
throw new InvalidOperationException($"Type not found: {trimmed}");
return null;
}
///
/// Finds scene components of a loaded component type by type name.
///
public static UComponent[] Components(string typeName, bool includeInactive = true)
{
return Components(Type(typeName), includeInactive);
}
///
/// Finds scene components of a loaded component type.
///
public static UComponent[] Components(Type componentType, bool includeInactive = true)
{
if (componentType == null)
throw new InvalidOperationException("Component type is null.");
if (!typeof(UComponent).IsAssignableFrom(componentType))
throw new InvalidOperationException($"{componentType.FullName} is not a Unity Component type.");
return FindComponentObjects(componentType, includeInactive)
.OfType()
.Where(c => c != null && !EditorUtility.IsPersistent(c) && c.gameObject.scene.IsValid())
.OrderBy(c => c.gameObject.scene.path, StringComparer.Ordinal)
.ThenBy(c => TransformPath(c.transform), StringComparer.Ordinal)
.ThenBy(c => c.GetInstanceID())
.ToArray();
}
private static UObject[] FindComponentObjects(Type componentType, bool includeInactive)
{
#if UNITY_6000_0_OR_NEWER
return UObject.FindObjectsByType(
componentType,
includeInactive ? FindObjectsInactive.Include : FindObjectsInactive.Exclude,
FindObjectsSortMode.None);
#else
return includeInactive
? Resources.FindObjectsOfTypeAll(componentType)
: UObject.FindObjectsOfType(componentType);
#endif
}
///
/// Reads a field or property from an object instance or a static type.
///
public static object Get(object targetOrType, string memberName)
{
var type = ResolveTarget(targetOrType, out var target, out var flags);
var field = FindField(type, memberName, flags);
if (field != null)
return field.GetValue(target);
var property = FindProperty(type, memberName, flags);
if (property != null)
return property.GetValue(target, null);
throw MissingMember(type, memberName);
}
///
/// Writes a field or property on an object instance or a static type.
///
public static void Set(object targetOrType, string memberName, object value)
{
var type = ResolveTarget(targetOrType, out var target, out var flags);
var field = FindField(type, memberName, flags);
if (field != null)
{
field.SetValue(target, ConvertValue(value, field.FieldType));
return;
}
var property = FindProperty(type, memberName, flags);
if (property != null && property.CanWrite)
{
property.SetValue(target, ConvertValue(value, property.PropertyType), null);
return;
}
throw MissingMember(type, memberName);
}
///
/// Calls a method on an object instance or a static type.
///
public static object Call(object targetOrType, string methodName, params object[] args)
{
var type = ResolveTarget(targetOrType, out var target, out var flags);
var arguments = args ?? Array.Empty