up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -1,160 +1,160 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ClassPath;
namespace StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders;
internal static class JavaServiceProviderScanner
{
public static JavaServiceProviderAnalysis Scan(JavaClassPathAnalysis classPath, JavaSpiCatalog catalog, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(classPath);
ArgumentNullException.ThrowIfNull(catalog);
var services = new Dictionary<string, ServiceAccumulator>(StringComparer.Ordinal);
foreach (var segment in classPath.Segments.OrderBy(static s => s.Order))
{
cancellationToken.ThrowIfCancellationRequested();
foreach (var kvp in segment.ServiceDefinitions)
{
cancellationToken.ThrowIfCancellationRequested();
if (kvp.Value.IsDefaultOrEmpty)
{
continue;
}
if (!services.TryGetValue(kvp.Key, out var accumulator))
{
accumulator = new ServiceAccumulator();
services[kvp.Key] = accumulator;
}
var providerIndex = 0;
foreach (var provider in kvp.Value)
{
var normalizedProvider = provider?.Trim();
if (string.IsNullOrEmpty(normalizedProvider))
{
providerIndex++;
continue;
}
accumulator.AddCandidate(new JavaServiceProviderCandidateRecord(
ProviderClass: normalizedProvider,
SegmentIdentifier: segment.Identifier,
SegmentOrder: segment.Order,
ProviderIndex: providerIndex++,
IsSelected: false));
}
}
}
var records = new List<JavaServiceProviderRecord>(services.Count);
foreach (var pair in services.OrderBy(static entry => entry.Key, StringComparer.Ordinal))
{
var descriptor = catalog.Resolve(pair.Key);
var accumulator = pair.Value;
var orderedCandidates = accumulator.GetOrderedCandidates();
if (orderedCandidates.Count == 0)
{
continue;
}
var selectedIndex = accumulator.DetermineSelection(orderedCandidates);
var warnings = accumulator.BuildWarnings();
var candidateArray = ImmutableArray.CreateRange(orderedCandidates.Select((candidate, index) =>
candidate with { IsSelected = index == selectedIndex }));
var warningsArray = warnings.Count == 0
? ImmutableArray<string>.Empty
: ImmutableArray.CreateRange(warnings);
records.Add(new JavaServiceProviderRecord(
ServiceId: pair.Key,
DisplayName: descriptor.DisplayName,
Category: descriptor.Category,
Candidates: candidateArray,
SelectedIndex: selectedIndex,
Warnings: warningsArray));
}
return new JavaServiceProviderAnalysis(records.ToImmutableArray());
}
private sealed class ServiceAccumulator
{
private readonly List<JavaServiceProviderCandidateRecord> _candidates = new();
private readonly Dictionary<string, HashSet<string>> _providerSources = new(StringComparer.Ordinal);
public void AddCandidate(JavaServiceProviderCandidateRecord candidate)
{
_candidates.Add(candidate);
if (!_providerSources.TryGetValue(candidate.ProviderClass, out var sources))
{
sources = new HashSet<string>(StringComparer.Ordinal);
_providerSources[candidate.ProviderClass] = sources;
}
sources.Add(candidate.SegmentIdentifier);
}
public IReadOnlyList<JavaServiceProviderCandidateRecord> GetOrderedCandidates()
=> _candidates
.OrderBy(static c => c.SegmentOrder)
.ThenBy(static c => c.ProviderIndex)
.ThenBy(static c => c.ProviderClass, StringComparer.Ordinal)
.ToList();
public int DetermineSelection(IReadOnlyList<JavaServiceProviderCandidateRecord> orderedCandidates)
=> orderedCandidates.Count == 0 ? -1 : 0;
public List<string> BuildWarnings()
{
var warnings = new List<string>();
foreach (var pair in _providerSources.OrderBy(static entry => entry.Key, StringComparer.Ordinal))
{
if (pair.Value.Count <= 1)
{
continue;
}
var locations = pair.Value
.OrderBy(static value => value, StringComparer.Ordinal)
.ToArray();
warnings.Add($"duplicate-provider: {pair.Key} ({string.Join(", ", locations)})");
}
return warnings;
}
}
}
internal sealed record JavaServiceProviderAnalysis(ImmutableArray<JavaServiceProviderRecord> Services)
{
public static readonly JavaServiceProviderAnalysis Empty = new(ImmutableArray<JavaServiceProviderRecord>.Empty);
}
internal sealed record JavaServiceProviderRecord(
string ServiceId,
string DisplayName,
string Category,
ImmutableArray<JavaServiceProviderCandidateRecord> Candidates,
int SelectedIndex,
ImmutableArray<string> Warnings);
internal sealed record JavaServiceProviderCandidateRecord(
string ProviderClass,
string SegmentIdentifier,
int SegmentOrder,
int ProviderIndex,
bool IsSelected);
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ClassPath;
namespace StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders;
internal static class JavaServiceProviderScanner
{
public static JavaServiceProviderAnalysis Scan(JavaClassPathAnalysis classPath, JavaSpiCatalog catalog, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(classPath);
ArgumentNullException.ThrowIfNull(catalog);
var services = new Dictionary<string, ServiceAccumulator>(StringComparer.Ordinal);
foreach (var segment in classPath.Segments.OrderBy(static s => s.Order))
{
cancellationToken.ThrowIfCancellationRequested();
foreach (var kvp in segment.ServiceDefinitions)
{
cancellationToken.ThrowIfCancellationRequested();
if (kvp.Value.IsDefaultOrEmpty)
{
continue;
}
if (!services.TryGetValue(kvp.Key, out var accumulator))
{
accumulator = new ServiceAccumulator();
services[kvp.Key] = accumulator;
}
var providerIndex = 0;
foreach (var provider in kvp.Value)
{
var normalizedProvider = provider?.Trim();
if (string.IsNullOrEmpty(normalizedProvider))
{
providerIndex++;
continue;
}
accumulator.AddCandidate(new JavaServiceProviderCandidateRecord(
ProviderClass: normalizedProvider,
SegmentIdentifier: segment.Identifier,
SegmentOrder: segment.Order,
ProviderIndex: providerIndex++,
IsSelected: false));
}
}
}
var records = new List<JavaServiceProviderRecord>(services.Count);
foreach (var pair in services.OrderBy(static entry => entry.Key, StringComparer.Ordinal))
{
var descriptor = catalog.Resolve(pair.Key);
var accumulator = pair.Value;
var orderedCandidates = accumulator.GetOrderedCandidates();
if (orderedCandidates.Count == 0)
{
continue;
}
var selectedIndex = accumulator.DetermineSelection(orderedCandidates);
var warnings = accumulator.BuildWarnings();
var candidateArray = ImmutableArray.CreateRange(orderedCandidates.Select((candidate, index) =>
candidate with { IsSelected = index == selectedIndex }));
var warningsArray = warnings.Count == 0
? ImmutableArray<string>.Empty
: ImmutableArray.CreateRange(warnings);
records.Add(new JavaServiceProviderRecord(
ServiceId: pair.Key,
DisplayName: descriptor.DisplayName,
Category: descriptor.Category,
Candidates: candidateArray,
SelectedIndex: selectedIndex,
Warnings: warningsArray));
}
return new JavaServiceProviderAnalysis(records.ToImmutableArray());
}
private sealed class ServiceAccumulator
{
private readonly List<JavaServiceProviderCandidateRecord> _candidates = new();
private readonly Dictionary<string, HashSet<string>> _providerSources = new(StringComparer.Ordinal);
public void AddCandidate(JavaServiceProviderCandidateRecord candidate)
{
_candidates.Add(candidate);
if (!_providerSources.TryGetValue(candidate.ProviderClass, out var sources))
{
sources = new HashSet<string>(StringComparer.Ordinal);
_providerSources[candidate.ProviderClass] = sources;
}
sources.Add(candidate.SegmentIdentifier);
}
public IReadOnlyList<JavaServiceProviderCandidateRecord> GetOrderedCandidates()
=> _candidates
.OrderBy(static c => c.SegmentOrder)
.ThenBy(static c => c.ProviderIndex)
.ThenBy(static c => c.ProviderClass, StringComparer.Ordinal)
.ToList();
public int DetermineSelection(IReadOnlyList<JavaServiceProviderCandidateRecord> orderedCandidates)
=> orderedCandidates.Count == 0 ? -1 : 0;
public List<string> BuildWarnings()
{
var warnings = new List<string>();
foreach (var pair in _providerSources.OrderBy(static entry => entry.Key, StringComparer.Ordinal))
{
if (pair.Value.Count <= 1)
{
continue;
}
var locations = pair.Value
.OrderBy(static value => value, StringComparer.Ordinal)
.ToArray();
warnings.Add($"duplicate-provider: {pair.Key} ({string.Join(", ", locations)})");
}
return warnings;
}
}
}
internal sealed record JavaServiceProviderAnalysis(ImmutableArray<JavaServiceProviderRecord> Services)
{
public static readonly JavaServiceProviderAnalysis Empty = new(ImmutableArray<JavaServiceProviderRecord>.Empty);
}
internal sealed record JavaServiceProviderRecord(
string ServiceId,
string DisplayName,
string Category,
ImmutableArray<JavaServiceProviderCandidateRecord> Candidates,
int SelectedIndex,
ImmutableArray<string> Warnings);
internal sealed record JavaServiceProviderCandidateRecord(
string ProviderClass,
string SegmentIdentifier,
int SegmentOrder,
int ProviderIndex,
bool IsSelected);

View File

@@ -1,103 +1,103 @@
using System.Collections.Immutable;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders;
internal sealed class JavaSpiCatalog
{
private static readonly Lazy<JavaSpiCatalog> LazyDefault = new(LoadDefaultCore);
private readonly ImmutableDictionary<string, JavaSpiDescriptor> _descriptors;
private JavaSpiCatalog(ImmutableDictionary<string, JavaSpiDescriptor> descriptors)
{
_descriptors = descriptors;
}
public static JavaSpiCatalog Default => LazyDefault.Value;
public JavaSpiDescriptor Resolve(string serviceId)
{
if (string.IsNullOrWhiteSpace(serviceId))
{
return JavaSpiDescriptor.CreateUnknown(string.Empty);
}
var key = serviceId.Trim();
if (_descriptors.TryGetValue(key, out var descriptor))
{
return descriptor;
}
return JavaSpiDescriptor.CreateUnknown(key);
}
private static JavaSpiCatalog LoadDefaultCore()
{
var assembly = typeof(JavaSpiCatalog).GetTypeInfo().Assembly;
var resourceName = "StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders.java-spi-catalog.json";
using var stream = assembly.GetManifestResourceStream(resourceName)
?? throw new InvalidOperationException($"Embedded SPI catalog '{resourceName}' not found.");
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, leaveOpen: false);
var json = reader.ReadToEnd();
var items = JsonSerializer.Deserialize<List<JavaSpiDescriptor>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
}) ?? new List<JavaSpiDescriptor>();
var descriptors = items
.Select(Normalize)
.Where(static item => !string.IsNullOrWhiteSpace(item.ServiceId))
.ToImmutableDictionary(
static item => item.ServiceId,
static item => item,
StringComparer.Ordinal);
return new JavaSpiCatalog(descriptors);
}
private static JavaSpiDescriptor Normalize(JavaSpiDescriptor descriptor)
{
var serviceId = descriptor.ServiceId?.Trim() ?? string.Empty;
var category = string.IsNullOrWhiteSpace(descriptor.Category)
? "unknown"
: descriptor.Category.Trim().ToLowerInvariant();
var displayName = string.IsNullOrWhiteSpace(descriptor.DisplayName)
? serviceId
: descriptor.DisplayName.Trim();
return descriptor with
{
ServiceId = serviceId,
Category = category,
DisplayName = displayName,
};
}
}
internal sealed record class JavaSpiDescriptor
{
[JsonPropertyName("serviceId")]
public string ServiceId { get; init; } = string.Empty;
[JsonPropertyName("category")]
public string Category { get; init; } = "unknown";
[JsonPropertyName("displayName")]
public string DisplayName { get; init; } = string.Empty;
[JsonPropertyName("notes")]
public string? Notes { get; init; }
public static JavaSpiDescriptor CreateUnknown(string serviceId)
=> new()
{
ServiceId = serviceId,
Category = "unknown",
DisplayName = serviceId,
};
}
using System.Collections.Immutable;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders;
internal sealed class JavaSpiCatalog
{
private static readonly Lazy<JavaSpiCatalog> LazyDefault = new(LoadDefaultCore);
private readonly ImmutableDictionary<string, JavaSpiDescriptor> _descriptors;
private JavaSpiCatalog(ImmutableDictionary<string, JavaSpiDescriptor> descriptors)
{
_descriptors = descriptors;
}
public static JavaSpiCatalog Default => LazyDefault.Value;
public JavaSpiDescriptor Resolve(string serviceId)
{
if (string.IsNullOrWhiteSpace(serviceId))
{
return JavaSpiDescriptor.CreateUnknown(string.Empty);
}
var key = serviceId.Trim();
if (_descriptors.TryGetValue(key, out var descriptor))
{
return descriptor;
}
return JavaSpiDescriptor.CreateUnknown(key);
}
private static JavaSpiCatalog LoadDefaultCore()
{
var assembly = typeof(JavaSpiCatalog).GetTypeInfo().Assembly;
var resourceName = "StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders.java-spi-catalog.json";
using var stream = assembly.GetManifestResourceStream(resourceName)
?? throw new InvalidOperationException($"Embedded SPI catalog '{resourceName}' not found.");
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, leaveOpen: false);
var json = reader.ReadToEnd();
var items = JsonSerializer.Deserialize<List<JavaSpiDescriptor>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
}) ?? new List<JavaSpiDescriptor>();
var descriptors = items
.Select(Normalize)
.Where(static item => !string.IsNullOrWhiteSpace(item.ServiceId))
.ToImmutableDictionary(
static item => item.ServiceId,
static item => item,
StringComparer.Ordinal);
return new JavaSpiCatalog(descriptors);
}
private static JavaSpiDescriptor Normalize(JavaSpiDescriptor descriptor)
{
var serviceId = descriptor.ServiceId?.Trim() ?? string.Empty;
var category = string.IsNullOrWhiteSpace(descriptor.Category)
? "unknown"
: descriptor.Category.Trim().ToLowerInvariant();
var displayName = string.IsNullOrWhiteSpace(descriptor.DisplayName)
? serviceId
: descriptor.DisplayName.Trim();
return descriptor with
{
ServiceId = serviceId,
Category = category,
DisplayName = displayName,
};
}
}
internal sealed record class JavaSpiDescriptor
{
[JsonPropertyName("serviceId")]
public string ServiceId { get; init; } = string.Empty;
[JsonPropertyName("category")]
public string Category { get; init; } = "unknown";
[JsonPropertyName("displayName")]
public string DisplayName { get; init; } = string.Empty;
[JsonPropertyName("notes")]
public string? Notes { get; init; }
public static JavaSpiDescriptor CreateUnknown(string serviceId)
=> new()
{
ServiceId = serviceId,
Category = "unknown",
DisplayName = serviceId,
};
}