@@ -0,0 +1,175 @@
|
||||
import 'package:jadb/models/kanjivg/kanjivg_entry.dart';
|
||||
import 'package:jadb/models/kanjivg/kanjivg_path.dart';
|
||||
import 'package:jadb/models/kanjivg/kanjivg_path_group.dart';
|
||||
import 'package:jadb/models/kanjivg/kanjivg_path_group_position.dart';
|
||||
import 'package:jadb/models/kanjivg/kanjivg_radical.dart';
|
||||
import 'package:jadb/table_names/kanjivg.dart';
|
||||
import 'package:sqflite_common/sqlite_api.dart';
|
||||
|
||||
Future<List<Map<String, Object?>>> _entryQuery(
|
||||
DatabaseExecutor connection,
|
||||
String entryKey,
|
||||
) => connection.rawQuery(
|
||||
'''
|
||||
SELECT *
|
||||
FROM "${KanjiVGTableNames.entry}"
|
||||
WHERE "character" = ?
|
||||
OR "character" LIKE ?
|
||||
ORDER BY "character" != ?, "character"
|
||||
LIMIT 1
|
||||
''',
|
||||
[entryKey, '$entryKey-%', entryKey],
|
||||
);
|
||||
|
||||
Future<List<Map<String, Object?>>> _pathsQuery(
|
||||
DatabaseExecutor connection,
|
||||
String entryKey,
|
||||
) => connection.rawQuery(
|
||||
'''
|
||||
SELECT
|
||||
"${KanjiVGTableNames.path}"."pathId",
|
||||
"${KanjiVGTableNames.path}"."groupId",
|
||||
"${KanjiVGTableNames.path}"."type",
|
||||
"${KanjiVGTableNames.path}"."svgPath",
|
||||
"${KanjiVGTableNames.strokeNumber}"."x",
|
||||
"${KanjiVGTableNames.strokeNumber}"."y"
|
||||
FROM "${KanjiVGTableNames.path}"
|
||||
JOIN "${KanjiVGTableNames.strokeNumber}"
|
||||
ON "${KanjiVGTableNames.path}"."character" = "${KanjiVGTableNames.strokeNumber}"."character"
|
||||
AND "${KanjiVGTableNames.path}"."pathId" = "${KanjiVGTableNames.strokeNumber}"."strokeNum"
|
||||
WHERE "${KanjiVGTableNames.path}"."character" = ?
|
||||
ORDER BY "${KanjiVGTableNames.path}"."pathId"
|
||||
''',
|
||||
[entryKey],
|
||||
);
|
||||
|
||||
Future<List<Map<String, Object?>>> _pathGroupsQuery(
|
||||
DatabaseExecutor connection,
|
||||
String entryKey,
|
||||
) => connection.query(
|
||||
KanjiVGTableNames.pathGroup,
|
||||
where: 'character = ?',
|
||||
whereArgs: [entryKey],
|
||||
orderBy: 'groupId',
|
||||
);
|
||||
|
||||
String _normalizeKanjiVGEntryKey(String kanji) {
|
||||
final encodedMatch = RegExp(r'^([0-9a-fA-F]{5,6})(-.+)?$').firstMatch(kanji);
|
||||
if (encodedMatch != null) {
|
||||
return '${encodedMatch.group(1)!.toLowerCase()}${encodedMatch.group(2) ?? ''}';
|
||||
}
|
||||
|
||||
final runes = kanji.runes.toList(growable: false);
|
||||
if (runes.length == 1) {
|
||||
return runes.single.toRadixString(16).padLeft(5, '0');
|
||||
}
|
||||
|
||||
return kanji;
|
||||
}
|
||||
|
||||
String _characterFromEntryKey(String entryKey, String fallback) {
|
||||
final encodedMatch = RegExp(r'^([0-9a-fA-F]{5,6})').firstMatch(entryKey);
|
||||
if (encodedMatch == null) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return String.fromCharCode(int.parse(encodedMatch.group(1)!, radix: 16));
|
||||
}
|
||||
|
||||
KanjiVGPath _pathFromRow(Map<String, Object?> row) => KanjiVGPath(
|
||||
pathId: row['pathId'] as int,
|
||||
type: row['type'] as String?,
|
||||
svgPath: row['svgPath'] as String,
|
||||
labelX: (row['x'] as num).toDouble(),
|
||||
labelY: (row['y'] as num).toDouble(),
|
||||
);
|
||||
|
||||
List<KanjiVGPathGroup> _buildPathGroups(
|
||||
List<Map<String, Object?>> pathRows,
|
||||
List<Map<String, Object?>> pathGroupRows,
|
||||
) {
|
||||
final rowsByGroupId = <int, Map<String, Object?>>{
|
||||
for (final row in pathGroupRows) (row['groupId'] as int?)!: row,
|
||||
};
|
||||
|
||||
final childGroupIdsByParentGroupId = <int?, List<int>>{};
|
||||
for (final row in pathGroupRows) {
|
||||
final groupId = (row['groupId'] as int?)!;
|
||||
final parentGroupId = row['parentGroupId'] as int?;
|
||||
childGroupIdsByParentGroupId
|
||||
.putIfAbsent(parentGroupId, () => [])
|
||||
.add(groupId);
|
||||
}
|
||||
|
||||
final pathsByGroupId = <int, List<KanjiVGPath>>{};
|
||||
for (final row in pathRows) {
|
||||
final groupId = (row['groupId'] as int?)!;
|
||||
pathsByGroupId.putIfAbsent(groupId, () => []).add(_pathFromRow(row));
|
||||
}
|
||||
|
||||
KanjiVGPathGroup buildGroup(int groupId) {
|
||||
final row = rowsByGroupId[groupId]!;
|
||||
|
||||
return KanjiVGPathGroup(
|
||||
groupId: groupId,
|
||||
paths: pathsByGroupId[groupId] ?? const [],
|
||||
children: (childGroupIdsByParentGroupId[groupId] ?? const [])
|
||||
.map(buildGroup)
|
||||
.toList(growable: false),
|
||||
element: row['element'] as String?,
|
||||
original: row['original'] as String?,
|
||||
position: KanjiVGPathGroupPosition.fromNullableString(
|
||||
row['position'] as String?,
|
||||
),
|
||||
radical: KanjiVGRadical.fromNullableString(row['radical'] as String?),
|
||||
part: row['part'] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
return (childGroupIdsByParentGroupId[null] ?? const [])
|
||||
.map(buildGroup)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
/// Searches for a KanjiVG graph and returns its stroke data, or null if the
|
||||
/// kanji is not found in the database.
|
||||
Future<KanjiVGEntry?> searchKanjiVGGraphWithDbConnection(
|
||||
DatabaseExecutor connection,
|
||||
String kanji, {
|
||||
bool includePathGroups = false,
|
||||
}) async {
|
||||
final entryKey = _normalizeKanjiVGEntryKey(kanji);
|
||||
final entryRows = await _entryQuery(connection, entryKey);
|
||||
|
||||
if (entryRows.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final matchedEntryKey = entryRows.first['character'] as String;
|
||||
|
||||
late final List<Map<String, Object?>> pathRows;
|
||||
List<Map<String, Object?>> pathGroupRows = const [];
|
||||
|
||||
if (includePathGroups) {
|
||||
await Future.wait([
|
||||
_pathsQuery(
|
||||
connection,
|
||||
matchedEntryKey,
|
||||
).then((value) => pathRows = value),
|
||||
_pathGroupsQuery(
|
||||
connection,
|
||||
matchedEntryKey,
|
||||
).then((value) => pathGroupRows = value),
|
||||
]);
|
||||
} else {
|
||||
pathRows = await _pathsQuery(connection, matchedEntryKey);
|
||||
}
|
||||
|
||||
return KanjiVGEntry(
|
||||
character: _characterFromEntryKey(matchedEntryKey, kanji),
|
||||
paths: pathRows.map(_pathFromRow).toList(growable: false),
|
||||
pathGroups: includePathGroups
|
||||
? _buildPathGroups(pathRows, pathGroupRows)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user