307 lines
9.0 KiB
Dart
307 lines
9.0 KiB
Dart
/// 引用解析器
|
||
/// 处理复杂嵌套类型和循环引用检测
|
||
library;
|
||
|
||
import 'package:logging/logging.dart';
|
||
import 'package:swagger_generator_flutter/core/models.dart';
|
||
|
||
/// 引用解析器
|
||
/// 负责处理 OpenAPI 文档中的复杂引用关系
|
||
class ReferenceResolver {
|
||
ReferenceResolver({this.maxDepth = 50});
|
||
|
||
static final Logger _logger = Logger('ReferenceResolver');
|
||
|
||
/// 已解析的模型缓存
|
||
final Map<String, ApiModel> _resolvedModels = {};
|
||
|
||
/// 当前解析路径(用于循环引用检测)
|
||
final Set<String> _resolutionPath = {};
|
||
|
||
/// 原始 JSON 数据缓存
|
||
final Map<String, Map<String, dynamic>> _rawSchemas = {};
|
||
|
||
/// 最大解析深度(防止过深的嵌套)
|
||
final int maxDepth;
|
||
|
||
/// 当前解析深度
|
||
int _currentDepth = 0;
|
||
|
||
/// 解析所有模型,处理复杂引用关系
|
||
Map<String, ApiModel> resolveModels(Map<String, dynamic> componentsJson) {
|
||
final schemasJson =
|
||
componentsJson['schemas'] as Map<String, dynamic>? ?? {};
|
||
|
||
// 第一步:缓存所有原始 schema 数据
|
||
_cacheRawSchemas(schemasJson);
|
||
|
||
// 第二步:解析所有模型
|
||
final resolvedModels = <String, ApiModel>{};
|
||
for (final schemaName in schemasJson.keys) {
|
||
try {
|
||
final model = resolveModel(schemaName);
|
||
if (model != null) {
|
||
resolvedModels[schemaName] = model;
|
||
}
|
||
} on Object catch (e) {
|
||
_logger.warning('⚠️ 解析模型 $schemaName 时发生错误: $e');
|
||
// 创建一个基本的模型作为后备
|
||
resolvedModels[schemaName] = ApiModel(
|
||
name: schemaName,
|
||
description: '解析失败的模型',
|
||
properties: {},
|
||
required: [],
|
||
);
|
||
}
|
||
}
|
||
|
||
return resolvedModels;
|
||
}
|
||
|
||
/// 缓存原始 schema 数据
|
||
void _cacheRawSchemas(Map<String, dynamic> schemasJson) {
|
||
schemasJson.forEach((name, schemaData) {
|
||
if (schemaData is Map<String, dynamic>) {
|
||
_rawSchemas[name] = schemaData;
|
||
}
|
||
});
|
||
}
|
||
|
||
/// 解析单个模型
|
||
ApiModel? resolveModel(String modelName) {
|
||
// 检查是否已经解析过
|
||
if (_resolvedModels.containsKey(modelName)) {
|
||
return _resolvedModels[modelName];
|
||
}
|
||
|
||
// 检查循环引用
|
||
if (_resolutionPath.contains(modelName)) {
|
||
_logger
|
||
.warning('🔄 检测到循环引用: ${_resolutionPath.join(' -> ')} -> $modelName');
|
||
return _createCircularReferenceModel(modelName);
|
||
}
|
||
|
||
// 检查解析深度
|
||
if (_currentDepth >= maxDepth) {
|
||
_logger.warning('⚠️ 达到最大解析深度 $maxDepth,停止解析 $modelName');
|
||
return _createDepthLimitModel(modelName);
|
||
}
|
||
|
||
// 获取原始数据
|
||
final schemaData = _rawSchemas[modelName];
|
||
if (schemaData == null) {
|
||
_logger.warning('⚠️ 未找到模型定义: $modelName');
|
||
return null;
|
||
}
|
||
|
||
// 开始解析
|
||
_resolutionPath.add(modelName);
|
||
_currentDepth++;
|
||
|
||
try {
|
||
final model = _parseModelWithContext(modelName, schemaData);
|
||
_resolvedModels[modelName] = model;
|
||
return model;
|
||
} finally {
|
||
_resolutionPath.remove(modelName);
|
||
_currentDepth--;
|
||
}
|
||
}
|
||
|
||
/// 在上下文中解析模型
|
||
ApiModel _parseModelWithContext(String name, Map<String, dynamic> json) {
|
||
// 检查是否是枚举类型
|
||
final isEnum = json['enum'] != null;
|
||
if (isEnum) {
|
||
return _parseEnumModel(name, json);
|
||
}
|
||
|
||
// 检查组合模式
|
||
if (json['allOf'] != null ||
|
||
json['oneOf'] != null ||
|
||
json['anyOf'] != null) {
|
||
return _parseCompositionModel(name, json);
|
||
}
|
||
|
||
// 解析普通对象模型
|
||
return _parseObjectModel(name, json);
|
||
}
|
||
|
||
/// 解析枚举模型
|
||
ApiModel _parseEnumModel(String name, Map<String, dynamic> json) {
|
||
final enumValues = List<dynamic>.from((json['enum'] as List?) ?? []);
|
||
final enumType =
|
||
PropertyType.fromString(json['type'] as String? ?? 'string');
|
||
|
||
return ApiModel(
|
||
name: name,
|
||
description: json['description'] as String? ?? '',
|
||
properties: {},
|
||
required: [],
|
||
isEnum: true,
|
||
enumValues: enumValues,
|
||
enumType: enumType,
|
||
);
|
||
}
|
||
|
||
/// 解析组合模式模型
|
||
ApiModel _parseCompositionModel(String name, Map<String, dynamic> json) {
|
||
// 解析组合模式
|
||
final allOf = _parseSchemaList(json['allOf'] as List<dynamic>? ?? []);
|
||
final oneOf = _parseSchemaList(json['oneOf'] as List<dynamic>? ?? []);
|
||
final anyOf = _parseSchemaList(json['anyOf'] as List<dynamic>? ?? []);
|
||
|
||
final notJson = json['not'] as Map<String, dynamic>?;
|
||
final not = notJson != null ? ApiSchema.fromJson(notJson) : null;
|
||
|
||
// 解析 discriminator
|
||
final discriminatorJson = json['discriminator'] as Map<String, dynamic>?;
|
||
final discriminator = discriminatorJson != null
|
||
? ApiDiscriminator.fromJson(discriminatorJson)
|
||
: null;
|
||
|
||
// 对于组合模式,我们需要合并属性
|
||
final mergedProperties = <String, ApiProperty>{};
|
||
final mergedRequired = <String>[];
|
||
|
||
// 从 allOf 中合并属性
|
||
for (final schema in allOf) {
|
||
_mergeSchemaProperties(schema, mergedProperties, mergedRequired);
|
||
}
|
||
|
||
// 如果有直接的 properties,也要合并
|
||
if (json['properties'] != null) {
|
||
final directProperties = _parseProperties(
|
||
json['properties'] as Map<String, dynamic>,
|
||
List<String>.from((json['required'] as List?) ?? []),
|
||
);
|
||
mergedProperties.addAll(directProperties);
|
||
mergedRequired
|
||
.addAll(List<String>.from((json['required'] as List?) ?? []));
|
||
}
|
||
|
||
return ApiModel(
|
||
name: name,
|
||
description: json['description'] as String? ?? '',
|
||
properties: mergedProperties,
|
||
required: mergedRequired.toSet().toList(),
|
||
allOf: allOf,
|
||
oneOf: oneOf,
|
||
anyOf: anyOf,
|
||
not: not,
|
||
discriminator: discriminator,
|
||
);
|
||
}
|
||
|
||
/// 解析对象模型
|
||
ApiModel _parseObjectModel(String name, Map<String, dynamic> json) {
|
||
final properties = _parseProperties(
|
||
json['properties'] as Map<String, dynamic>? ?? {},
|
||
List<String>.from((json['required'] as List?) ?? []),
|
||
);
|
||
|
||
final required = List<String>.from((json['required'] as List?) ?? []);
|
||
|
||
return ApiModel(
|
||
name: name,
|
||
description: json['description'] as String? ?? '',
|
||
properties: properties,
|
||
required: required,
|
||
);
|
||
}
|
||
|
||
/// 解析 schema 列表
|
||
List<ApiSchema> _parseSchemaList(List<dynamic> schemaList) {
|
||
return schemaList
|
||
.map((schema) => ApiSchema.fromJson(schema as Map<String, dynamic>))
|
||
.toList();
|
||
}
|
||
|
||
/// 合并 schema 的属性
|
||
void _mergeSchemaProperties(
|
||
ApiSchema schema,
|
||
Map<String, ApiProperty> targetProperties,
|
||
List<String> targetRequired,
|
||
) {
|
||
// 如果是引用,解析引用的模型
|
||
if (schema.isReference && schema.reference != null) {
|
||
final referencedModel = resolveModel(schema.reference!);
|
||
if (referencedModel != null) {
|
||
targetProperties.addAll(referencedModel.properties);
|
||
targetRequired.addAll(referencedModel.required);
|
||
}
|
||
} else {
|
||
// 直接合并属性
|
||
targetProperties.addAll(schema.properties);
|
||
targetRequired.addAll(schema.required);
|
||
}
|
||
}
|
||
|
||
/// 解析属性
|
||
Map<String, ApiProperty> _parseProperties(
|
||
Map<String, dynamic> propertiesJson,
|
||
List<String> requiredFields,
|
||
) {
|
||
final properties = <String, ApiProperty>{};
|
||
|
||
propertiesJson.forEach((propName, propData) {
|
||
if (propData is Map<String, dynamic>) {
|
||
try {
|
||
final property =
|
||
_parsePropertyWithContext(propName, propData, requiredFields);
|
||
properties[propName] = property;
|
||
} on Object catch (e) {
|
||
_logger.warning('⚠️ 解析属性 $propName 时发生错误: $e');
|
||
// 创建一个基本属性作为后备
|
||
properties[propName] = ApiProperty(
|
||
name: propName,
|
||
type: PropertyType.string,
|
||
description: '解析失败的属性',
|
||
required: requiredFields.contains(propName),
|
||
);
|
||
}
|
||
}
|
||
});
|
||
|
||
return properties;
|
||
}
|
||
|
||
/// 在上下文中解析属性
|
||
ApiProperty _parsePropertyWithContext(
|
||
String name,
|
||
Map<String, dynamic> json,
|
||
List<String> requiredFields,
|
||
) {
|
||
// 使用现有的 ApiProperty.fromJson,但在循环引用检测的上下文中
|
||
return ApiProperty.fromJson(name, json, requiredFields);
|
||
}
|
||
|
||
/// 创建循环引用模型
|
||
ApiModel _createCircularReferenceModel(String modelName) {
|
||
return ApiModel(
|
||
name: modelName,
|
||
description: '循环引用模型 - 为避免无限递归而创建的占位符',
|
||
properties: {},
|
||
required: [],
|
||
);
|
||
}
|
||
|
||
/// 创建深度限制模型
|
||
ApiModel _createDepthLimitModel(String modelName) {
|
||
return ApiModel(
|
||
name: modelName,
|
||
description: '深度限制模型 - 达到最大解析深度而创建的占位符',
|
||
properties: {},
|
||
required: [],
|
||
);
|
||
}
|
||
|
||
/// 清理缓存
|
||
void clearCache() {
|
||
_resolvedModels.clear();
|
||
_resolutionPath.clear();
|
||
_rawSchemas.clear();
|
||
_currentDepth = 0;
|
||
}
|
||
}
|