v10
This commit is contained in:
@@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
import 'v10/app.dart';
|
import 'v11/app.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|||||||
@@ -147,7 +147,6 @@ class CellIRoute extends IRouteNode {
|
|||||||
if (pageBuilder != null) {
|
if (pageBuilder != null) {
|
||||||
return pageBuilder!(context, config, child);
|
return pageBuilder!(context, config, child);
|
||||||
}
|
}
|
||||||
print("======CellIRoute#createCellPage${config.pageKey}=================");
|
|
||||||
return MaterialPage(
|
return MaterialPage(
|
||||||
child: child,
|
child: child,
|
||||||
key: config.pageKey,
|
key: config.pageKey,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class AppRouterDelegate extends RouterDelegate<IRouteConfig>
|
|||||||
/// 核心数据,路由配置数据列表
|
/// 核心数据,路由配置数据列表
|
||||||
final List<IRouteConfig> _configs = [];
|
final List<IRouteConfig> _configs = [];
|
||||||
|
|
||||||
String get path => current.uri.path;
|
String get path => current.uri.toString();
|
||||||
|
|
||||||
IRouteConfig get current => _configs.last;
|
IRouteConfig get current => _configs.last;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:iroute/v9/app/navigation/router/views/navigator_scope.dart';
|
import 'package:iroute/v9/app/navigation/router/views/navigator_scope.dart';
|
||||||
|
|
||||||
import 'iroute_config.dart';
|
import 'iroute_config.dart';
|
||||||
|
import 'utils/path_utils.dart';
|
||||||
|
|
||||||
typedef IRoutePageBuilder = Page? Function(
|
typedef IRoutePageBuilder = Page? Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
@@ -55,7 +56,7 @@ abstract class IRouteNode {
|
|||||||
if (node.children.isNotEmpty) {
|
if (node.children.isNotEmpty) {
|
||||||
target = prefix + target;
|
target = prefix + target;
|
||||||
|
|
||||||
List<IRouteNode> nodes = node.children.where((e) => e.path == target).toList();
|
List<IRouteNode> nodes = node.children.where((e) => _match(e.path,target)).toList();
|
||||||
bool match = nodes.isNotEmpty;
|
bool match = nodes.isNotEmpty;
|
||||||
if (match) {
|
if (match) {
|
||||||
IRouteNode matched = nodes.first;
|
IRouteNode matched = nodes.first;
|
||||||
@@ -69,6 +70,14 @@ abstract class IRouteNode {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _match(String path ,String target){
|
||||||
|
if(!path.contains(':')){
|
||||||
|
return path == target;
|
||||||
|
}
|
||||||
|
return patternToRegExp(path,[]).hasMatch(target);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 优先调用 [pageBuilder] 构建 Page
|
/// 优先调用 [pageBuilder] 构建 Page
|
||||||
@@ -147,7 +156,6 @@ class CellIRoute extends IRouteNode {
|
|||||||
if (pageBuilder != null) {
|
if (pageBuilder != null) {
|
||||||
return pageBuilder!(context, config, child);
|
return pageBuilder!(context, config, child);
|
||||||
}
|
}
|
||||||
print("======CellIRoute#createCellPage${config.pageKey}=================");
|
|
||||||
return MaterialPage(
|
return MaterialPage(
|
||||||
child: child,
|
child: child,
|
||||||
key: config.pageKey,
|
key: config.pageKey,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class IRouteConfig {
|
|||||||
final Object? extra;
|
final Object? extra;
|
||||||
final bool forResult;
|
final bool forResult;
|
||||||
final Uri uri;
|
final Uri uri;
|
||||||
|
final Map<String,String>? pathParams;
|
||||||
final bool keepAlive;
|
final bool keepAlive;
|
||||||
final RouteStyle routeStyle;
|
final RouteStyle routeStyle;
|
||||||
final bool recordHistory;
|
final bool recordHistory;
|
||||||
@@ -18,12 +19,13 @@ class IRouteConfig {
|
|||||||
this.extra,
|
this.extra,
|
||||||
required this.uri,
|
required this.uri,
|
||||||
this.forResult = false,
|
this.forResult = false,
|
||||||
|
this.pathParams,
|
||||||
this.keepAlive = false,
|
this.keepAlive = false,
|
||||||
this.routeStyle = RouteStyle.replace,
|
this.routeStyle = RouteStyle.replace,
|
||||||
this.recordHistory = false,
|
this.recordHistory = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
String get path => uri.path;
|
String get path => uri.toString();
|
||||||
|
|
||||||
IRouteConfig copyWith({
|
IRouteConfig copyWith({
|
||||||
Object? extra,
|
Object? extra,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ CellIRoute appRoute = CellIRoute(
|
|||||||
widget: SortSettings(),
|
widget: SortSettings(),
|
||||||
),
|
),
|
||||||
const IRoute(
|
const IRoute(
|
||||||
path: '/app/sort/player',
|
path: '/app/sort/player/:name',
|
||||||
widget: SortPlayer(),
|
widget: SortPlayer(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -80,7 +80,7 @@ Map<String, String> kRouteLabelMap = {
|
|||||||
'/app/color/detail': '颜色详情',
|
'/app/color/detail': '颜色详情',
|
||||||
'/app/counter': '计数器',
|
'/app/counter': '计数器',
|
||||||
'/app/sort': '排序算法',
|
'/app/sort': '排序算法',
|
||||||
'/app/sort/player': '演示',
|
'/app/sort/:name': '',
|
||||||
'/app/sort/settings': '排序配置',
|
'/app/sort/settings': '排序配置',
|
||||||
'/app/user': '我的',
|
'/app/user': '我的',
|
||||||
'/app/settings': '系统设置',
|
'/app/settings': '系统设置',
|
||||||
|
|||||||
37
lib/v11/app/navigation/router/utils/path_utils.dart
Normal file
37
lib/v11/app/navigation/router/utils/path_utils.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?');
|
||||||
|
|
||||||
|
RegExp patternToRegExp(String pattern, List<String> parameters) {
|
||||||
|
final StringBuffer buffer = StringBuffer('^');
|
||||||
|
int start = 0;
|
||||||
|
for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) {
|
||||||
|
if (match.start > start) {
|
||||||
|
buffer.write(RegExp.escape(pattern.substring(start, match.start)));
|
||||||
|
}
|
||||||
|
final String name = match[1]!;
|
||||||
|
final String? optionalPattern = match[2];
|
||||||
|
final String regex = optionalPattern != null
|
||||||
|
? _escapeGroup(optionalPattern, name)
|
||||||
|
: '(?<$name>[^/]+)';
|
||||||
|
buffer.write(regex);
|
||||||
|
parameters.add(name);
|
||||||
|
start = match.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < pattern.length) {
|
||||||
|
buffer.write(RegExp.escape(pattern.substring(start)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pattern.endsWith('/')) {
|
||||||
|
buffer.write(r'(?=/|$)');
|
||||||
|
}
|
||||||
|
return RegExp(buffer.toString(), caseSensitive: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _escapeGroup(String group, [String? name]) {
|
||||||
|
final String escapedGroup = group.replaceFirstMapped(
|
||||||
|
RegExp(r'[:=!]'), (Match match) => '\\${match[0]}');
|
||||||
|
if (name != null) {
|
||||||
|
return '(?<$name>$escapedGroup)';
|
||||||
|
}
|
||||||
|
return escapedGroup;
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:iroute/components/components.dart';
|
import 'package:iroute/components/components.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import '../../../pages/sort/provider/state.dart';
|
||||||
import '../router/app_router_delegate.dart';
|
import '../router/app_router_delegate.dart';
|
||||||
import '../router/iroute_config.dart';
|
import '../router/iroute_config.dart';
|
||||||
import '../router/routes.dart';
|
import '../router/routes.dart';
|
||||||
@@ -37,7 +39,7 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
|
|||||||
tail: Padding(
|
tail: Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 6.0),
|
padding: const EdgeInsets.only(bottom: 6.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'V0.0.10',
|
'V0.0.11',
|
||||||
style: TextStyle(color: Colors.white, fontSize: 12),
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -66,6 +68,12 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
|
|||||||
router.changePath(path, keepAlive: true,recordHistory: true);
|
router.changePath(path, keepAlive: true,recordHistory: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(index ==2){
|
||||||
|
SortState state = SortStateScope.read(context);
|
||||||
|
String name = state.config.name;
|
||||||
|
router.changePath('/app/sort/$name',recordHistory: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (index == 4) {
|
if (index == 4) {
|
||||||
router.changePath(path, style: RouteStyle.push,recordHistory: true);
|
router.changePath(path, style: RouteStyle.push,recordHistory: true);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:iroute/components/components.dart';
|
import 'package:iroute/components/components.dart';
|
||||||
|
import '../../../../pages/sort/provider/state.dart';
|
||||||
import '../../router/app_router_delegate.dart';
|
import '../../router/app_router_delegate.dart';
|
||||||
import '../../router/routes.dart';
|
import '../../router/routes.dart';
|
||||||
|
import '../../router/utils/path_utils.dart';
|
||||||
import '../../router/views/route_back_indicator.dart';
|
import '../../router/views/route_back_indicator.dart';
|
||||||
import 'app_router_editor.dart';
|
import 'app_router_editor.dart';
|
||||||
import 'history_view_icon.dart';
|
import 'history_view_icon.dart';
|
||||||
@@ -83,6 +85,13 @@ class _RouterIndicatorState extends State<RouterIndicator> {
|
|||||||
|
|
||||||
void _onRouterChange() {
|
void _onRouterChange() {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
if(router.path.startsWith('/app/sort/')){
|
||||||
|
SortState state = SortStateScope.of(context);
|
||||||
|
String name = router.path.replaceAll('/app/sort/', '');
|
||||||
|
if(name!='settings'){
|
||||||
|
state.selectName(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BreadcrumbItem> pathToBreadcrumbItems(String path) {
|
List<BreadcrumbItem> pathToBreadcrumbItems(String path) {
|
||||||
@@ -97,7 +106,12 @@ class _RouterIndicatorState extends State<RouterIndicator> {
|
|||||||
|
|
||||||
for (String segment in uri.pathSegments) {
|
for (String segment in uri.pathSegments) {
|
||||||
to += '/$segment';
|
to += '/$segment';
|
||||||
String label = kRouteLabelMap[to] ?? '未知路由';
|
String label = '';
|
||||||
|
if(to.startsWith('/app/sort/')){
|
||||||
|
label = to.replaceAll('/app/sort/', '');
|
||||||
|
}else{
|
||||||
|
label = kRouteLabelMap[to] ?? '未知路由';
|
||||||
|
}
|
||||||
if(label.isNotEmpty){
|
if(label.isNotEmpty){
|
||||||
result.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
|
result.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,9 +48,10 @@ class _ColorPageState extends State<ColorPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _selectColor(Color color){
|
void _selectColor(Color color){
|
||||||
// String value = color.value.toRadixString(16);
|
String value = color.value.toRadixString(16);
|
||||||
// router.path = '/color/detail?color=$value';
|
String path = '/app/color/detail?color=$value';
|
||||||
router.changePath('/app/color/detail',extra: color);
|
// router.changePath('/app/color/detail',extra: color);
|
||||||
|
router.changePath(path,recordHistory: true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,4 +98,7 @@ class SortStateScope extends InheritedNotifier<SortState> {
|
|||||||
|
|
||||||
static SortState of(BuildContext context) =>
|
static SortState of(BuildContext context) =>
|
||||||
context.dependOnInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
context.dependOnInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
||||||
|
|
||||||
|
static SortState read(BuildContext context) =>
|
||||||
|
context.getInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ class SortRailPanel extends StatelessWidget {
|
|||||||
options: sortNameMap.values.toList(),
|
options: sortNameMap.values.toList(),
|
||||||
onSelected: (name) {
|
onSelected: (name) {
|
||||||
state.selectName(name);
|
state.selectName(name);
|
||||||
router.changePath('/app/sort/player');
|
router.changePath('/app/sort/${name}',recordHistory: true);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -148,7 +148,6 @@ class CellIRoute extends IRouteNode {
|
|||||||
if (pageBuilder != null) {
|
if (pageBuilder != null) {
|
||||||
return pageBuilder!(context, config, child);
|
return pageBuilder!(context, config, child);
|
||||||
}
|
}
|
||||||
print("======CellIRoute#createCellPage${config.pageKey}=================");
|
|
||||||
return MaterialPage(
|
return MaterialPage(
|
||||||
child: child,
|
child: child,
|
||||||
key: config.pageKey,
|
key: config.pageKey,
|
||||||
|
|||||||
39
test/algorithm/linked_list/206_reverse_list.dart
Normal file
39
test/algorithm/linked_list/206_reverse_list.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
void main(){
|
||||||
|
/// 打印 1->9->9->4
|
||||||
|
ListNode listNode = ListNode(1, ListNode(9, ListNode(9,ListNode(4))));
|
||||||
|
print(listNode);
|
||||||
|
ListNode? result = listNode.reverseList(listNode);
|
||||||
|
print(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListNode {
|
||||||
|
int val;
|
||||||
|
ListNode? next;
|
||||||
|
ListNode([this.val = 0, this.next]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString(){
|
||||||
|
ListNode? cur = this;
|
||||||
|
String result = "";
|
||||||
|
while( cur!=null){
|
||||||
|
result = result +cur.val.toString();
|
||||||
|
cur = cur.next;
|
||||||
|
if(cur!=null){
|
||||||
|
result +='->';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListNode? reverseList(ListNode? head) {
|
||||||
|
if (head == null || head.next == null) return head;
|
||||||
|
ListNode? pre = reverseList(head.next);
|
||||||
|
head.next?.next = head;
|
||||||
|
head.next = null;
|
||||||
|
return pre;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
158
test/parser/path_utils.dart
Normal file
158
test/parser/path_utils.dart
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?');
|
||||||
|
|
||||||
|
/// Converts a [pattern] such as `/user/:id` into [RegExp].
|
||||||
|
///
|
||||||
|
/// The path parameters can be specified by prefixing them with `:`. The
|
||||||
|
/// `parameters` are used for storing path parameter names.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// `pattern` = `/user/:id/book/:bookId`
|
||||||
|
///
|
||||||
|
/// The `parameters` would contain `['id', 'bookId']` as a result of calling
|
||||||
|
/// this method.
|
||||||
|
///
|
||||||
|
/// To extract the path parameter values from a [RegExpMatch], pass the
|
||||||
|
/// [RegExpMatch] into [extractPathParameters] with the `parameters` that are
|
||||||
|
/// used for generating the [RegExp].
|
||||||
|
RegExp patternToRegExp(String pattern, List<String> parameters) {
|
||||||
|
final StringBuffer buffer = StringBuffer('^');
|
||||||
|
int start = 0;
|
||||||
|
for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) {
|
||||||
|
if (match.start > start) {
|
||||||
|
buffer.write(RegExp.escape(pattern.substring(start, match.start)));
|
||||||
|
}
|
||||||
|
final String name = match[1]!;
|
||||||
|
final String? optionalPattern = match[2];
|
||||||
|
final String regex = optionalPattern != null
|
||||||
|
? _escapeGroup(optionalPattern, name)
|
||||||
|
: '(?<$name>[^/]+)';
|
||||||
|
buffer.write(regex);
|
||||||
|
parameters.add(name);
|
||||||
|
start = match.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < pattern.length) {
|
||||||
|
buffer.write(RegExp.escape(pattern.substring(start)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pattern.endsWith('/')) {
|
||||||
|
buffer.write(r'(?=/|$)');
|
||||||
|
}
|
||||||
|
return RegExp(buffer.toString(), caseSensitive: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _escapeGroup(String group, [String? name]) {
|
||||||
|
final String escapedGroup = group.replaceFirstMapped(
|
||||||
|
RegExp(r'[:=!]'), (Match match) => '\\${match[0]}');
|
||||||
|
if (name != null) {
|
||||||
|
return '(?<$name>$escapedGroup)';
|
||||||
|
}
|
||||||
|
return escapedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reconstructs the full path from a [pattern] and path parameters.
|
||||||
|
///
|
||||||
|
/// This is useful for restoring the original path from a [RegExpMatch].
|
||||||
|
///
|
||||||
|
/// For example, A path matched a [RegExp] returned from [patternToRegExp] and
|
||||||
|
/// produced a [RegExpMatch]. To reconstruct the path from the match, one
|
||||||
|
/// can follow these steps:
|
||||||
|
///
|
||||||
|
/// 1. Get the `pathParameters` by calling [extractPathParameters] with the
|
||||||
|
/// [RegExpMatch] and the parameters used for generating the [RegExp].
|
||||||
|
/// 2. Call [patternToPath] with the `pathParameters` from the first step and
|
||||||
|
/// the original `pattern` used for generating the [RegExp].
|
||||||
|
String patternToPath(String pattern, Map<String, String> pathParameters) {
|
||||||
|
final StringBuffer buffer = StringBuffer();
|
||||||
|
int start = 0;
|
||||||
|
for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) {
|
||||||
|
if (match.start > start) {
|
||||||
|
buffer.write(pattern.substring(start, match.start));
|
||||||
|
}
|
||||||
|
final String name = match[1]!;
|
||||||
|
buffer.write(pathParameters[name]);
|
||||||
|
start = match.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < pattern.length) {
|
||||||
|
buffer.write(pattern.substring(start));
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts arguments from the `match` and maps them by parameter name.
|
||||||
|
///
|
||||||
|
/// The [parameters] should originate from the call to [patternToRegExp] that
|
||||||
|
/// creates the [RegExp].
|
||||||
|
Map<String, String> extractPathParameters(
|
||||||
|
List<String> parameters, RegExpMatch match) {
|
||||||
|
return <String, String>{
|
||||||
|
for (int i = 0; i < parameters.length; ++i)
|
||||||
|
parameters[i]: match.namedGroup(parameters[i])!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Concatenates two paths.
|
||||||
|
///
|
||||||
|
/// e.g: pathA = /a, pathB = c/d, concatenatePaths(pathA, pathB) = /a/c/d.
|
||||||
|
String concatenatePaths(String parentPath, String childPath) {
|
||||||
|
// at the root, just return the path
|
||||||
|
if (parentPath.isEmpty) {
|
||||||
|
assert(childPath.startsWith('/'));
|
||||||
|
assert(childPath == '/' || !childPath.endsWith('/'));
|
||||||
|
return childPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not at the root, so append the parent path
|
||||||
|
assert(childPath.isNotEmpty);
|
||||||
|
assert(!childPath.startsWith('/'));
|
||||||
|
assert(!childPath.endsWith('/'));
|
||||||
|
return '${parentPath == '/' ? '' : parentPath}/$childPath';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalizes the location string.
|
||||||
|
String canonicalUri(String loc) {
|
||||||
|
String canon = Uri.parse(loc).toString();
|
||||||
|
canon = canon.endsWith('?') ? canon.substring(0, canon.length - 1) : canon;
|
||||||
|
|
||||||
|
// remove trailing slash except for when you shouldn't, e.g.
|
||||||
|
// /profile/ => /profile
|
||||||
|
// / => /
|
||||||
|
// /login?from=/ => login?from=/
|
||||||
|
canon = canon.endsWith('/') && canon != '/' && !canon.contains('?')
|
||||||
|
? canon.substring(0, canon.length - 1)
|
||||||
|
: canon;
|
||||||
|
|
||||||
|
// /login/?from=/ => /login?from=/
|
||||||
|
// /?from=/ => /?from=/
|
||||||
|
canon = canon.replaceFirst('/?', '?', 1);
|
||||||
|
|
||||||
|
return canon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds an absolute path for the provided route.
|
||||||
|
// String? fullPathForRoute(
|
||||||
|
// RouteBase targetRoute, String parentFullpath, List<RouteBase> routes) {
|
||||||
|
// for (final RouteBase route in routes) {
|
||||||
|
// final String fullPath = (route is GoRoute)
|
||||||
|
// ? concatenatePaths(parentFullpath, route.path)
|
||||||
|
// : parentFullpath;
|
||||||
|
//
|
||||||
|
// if (route == targetRoute) {
|
||||||
|
// return fullPath;
|
||||||
|
// } else {
|
||||||
|
// final String? subRoutePath =
|
||||||
|
// fullPathForRoute(targetRoute, fullPath, route.routes);
|
||||||
|
// if (subRoutePath != null) {
|
||||||
|
// return subRoutePath;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
107
test/parser/uri.dart
Normal file
107
test/parser/uri.dart
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import 'path_utils.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
String pattern = '/user/:id/book/:bookId';
|
||||||
|
String path = '/user/001/book/card';
|
||||||
|
|
||||||
|
parserPath();
|
||||||
|
// List<String> params = [];
|
||||||
|
// RegExp regExp = patternToRegExp(pattern,params);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// bool data = regExp.hasMatch(path);
|
||||||
|
// List<RegExpMatch> matchs = regExp.allMatches(path).toList();
|
||||||
|
// Map<String,String?> pathParams = {};
|
||||||
|
// if(matchs.isNotEmpty){
|
||||||
|
//
|
||||||
|
// for(int i=0;i<params.length;i++){
|
||||||
|
// String? value = matchs.first.namedGroup(params[i]);
|
||||||
|
// pathParams[params[i]] = value;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// print(pathParams);
|
||||||
|
// print(regExp.pattern);
|
||||||
|
//
|
||||||
|
// // match.forEach((element) {
|
||||||
|
// // print(element.namedGroup('id'));
|
||||||
|
// // });
|
||||||
|
// // regExp
|
||||||
|
// print(params);
|
||||||
|
// String ret = patternToPath(pattern, {
|
||||||
|
// 'id':'0',
|
||||||
|
// 'bookId':'card',
|
||||||
|
// });
|
||||||
|
// print(regExp);
|
||||||
|
// print(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parserPath() {
|
||||||
|
String template = '/user/:id/book/:bookId';
|
||||||
|
String path = '/user/001/book/card';
|
||||||
|
List<String> keys = [];
|
||||||
|
RegExp regExp = patternToRegExp(template,keys);
|
||||||
|
Map<String, String?> pathParams = {};
|
||||||
|
RegExpMatch? match = regExp.firstMatch(path);
|
||||||
|
|
||||||
|
if (match != null) {
|
||||||
|
for (int i = 0; i < keys.length; i++) {
|
||||||
|
String? value = match.namedGroup(keys[i]);
|
||||||
|
pathParams[keys[i]] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print(pathParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?');
|
||||||
|
|
||||||
|
RegExp patternToRegExp(String pattern, List<String> parameters) {
|
||||||
|
final StringBuffer buffer = StringBuffer('^');
|
||||||
|
int start = 0;
|
||||||
|
for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) {
|
||||||
|
if (match.start > start) {
|
||||||
|
buffer.write(RegExp.escape(pattern.substring(start, match.start)));
|
||||||
|
}
|
||||||
|
final String name = match[1]!;
|
||||||
|
final String? optionalPattern = match[2];
|
||||||
|
final String regex = optionalPattern != null
|
||||||
|
? _escapeGroup(optionalPattern, name)
|
||||||
|
: '(?<$name>[^/]+)';
|
||||||
|
buffer.write(regex);
|
||||||
|
parameters.add(name);
|
||||||
|
start = match.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < pattern.length) {
|
||||||
|
buffer.write(RegExp.escape(pattern.substring(start)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pattern.endsWith('/')) {
|
||||||
|
buffer.write(r'(?=/|$)');
|
||||||
|
}
|
||||||
|
return RegExp(buffer.toString(), caseSensitive: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _escapeGroup(String group, [String? name]) {
|
||||||
|
final String escapedGroup = group.replaceFirstMapped(
|
||||||
|
RegExp(r'[:=!]'), (Match match) => '\\${match[0]}');
|
||||||
|
if (name != null) {
|
||||||
|
return '(?<$name>$escapedGroup)';
|
||||||
|
}
|
||||||
|
return escapedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parser() {
|
||||||
|
String path =
|
||||||
|
'http://user:pwd@toly1994.com:8080/path1/path2?key1=value1&key2=value2#fragment';
|
||||||
|
Uri? uri = Uri.tryParse(path);
|
||||||
|
if (uri == null) return;
|
||||||
|
print(uri.scheme); // http
|
||||||
|
print(uri.userInfo); // user:pwd
|
||||||
|
print(uri.host); // toly1994.com
|
||||||
|
print(uri.port); // 8080
|
||||||
|
print(uri.path); // /path1/path2
|
||||||
|
print(uri.query); // key1=value1&key2=value2
|
||||||
|
print(uri.fragment); // fragment
|
||||||
|
}
|
||||||
204
test/structure/link_list/find.dart
Normal file
204
test/structure/link_list/find.dart
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
/// Dart、Rust 、C++ 、Java
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
ListNode listNode = ListNode(0, ListNode(3, ListNode(2, ListNode(8))));
|
||||||
|
print(listNode);
|
||||||
|
|
||||||
|
// List<int> evens = listNode.getEvenValue();
|
||||||
|
// print(evens);
|
||||||
|
|
||||||
|
// List<int> values = listNode.getValueByStep(2);
|
||||||
|
// print(values);
|
||||||
|
|
||||||
|
// listNode.deleteAt(3);
|
||||||
|
// ListNode? ret = listNode.deleteFromEnd(2);
|
||||||
|
// print(ret);
|
||||||
|
|
||||||
|
// ListNode? middleNode1 = listNode.middleNode1();
|
||||||
|
// print(middleNode1);
|
||||||
|
|
||||||
|
ListNode? middleNode2 = listNode.middleNode2();
|
||||||
|
print(middleNode2);
|
||||||
|
// ListNode? findNode = listNode.find(8);
|
||||||
|
// if(findNode!=null){
|
||||||
|
// ListNode? result = listNode.deleteNode(findNode);
|
||||||
|
// // 0->2->8
|
||||||
|
// print(result);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListNode {
|
||||||
|
int val;
|
||||||
|
ListNode? next;
|
||||||
|
ListNode([this.val = 0, this.next]);
|
||||||
|
|
||||||
|
//// region [2023.11.20]
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
ListNode? cur = this;
|
||||||
|
String result = "";
|
||||||
|
while (cur != null) {
|
||||||
|
result = result + cur.val.toString();
|
||||||
|
cur = cur.next;
|
||||||
|
if (cur != null) {
|
||||||
|
result += '->';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
//// region [2023.11.21]
|
||||||
|
/// [2023.11.21] TODO: 完成 find 函数,在链表中查找指定值的首位节点
|
||||||
|
ListNode? find(int target) {
|
||||||
|
ListNode? cur = this;
|
||||||
|
while (cur != null) {
|
||||||
|
if (cur.val == target) {
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
cur = cur.next;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [2023.11.21]
|
||||||
|
/// TODO: LeetCode 237 删除指定节点(非尾结点)
|
||||||
|
void deleteNodeNotLast(ListNode node) {
|
||||||
|
if (node.next != null) {
|
||||||
|
node.val = node.next!.val;
|
||||||
|
node.next = node.next?.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
//// region [2023.11.22]
|
||||||
|
/// [2023.11.22]
|
||||||
|
/// TODO: 完成 size 方法,返回链表长度
|
||||||
|
int size() {
|
||||||
|
int i = 0;
|
||||||
|
ListNode? cur = this;
|
||||||
|
while (cur != null) {
|
||||||
|
cur = cur.next;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [2023.11.22]
|
||||||
|
/// TODO: 完成 deleteAt 方法,删除第 index 个节点
|
||||||
|
void deleteAt(int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
ListNode? next = this.next;
|
||||||
|
this.val = next?.val ?? -1;
|
||||||
|
this.next = next?.next;
|
||||||
|
next?.next = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListNode? cur = this;
|
||||||
|
for (int i = 0; i < index - 1; i++) {
|
||||||
|
cur = cur?.next;
|
||||||
|
}
|
||||||
|
ListNode? target = cur?.next;
|
||||||
|
cur?.next = target?.next;
|
||||||
|
target?.next = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [2023.11.22]
|
||||||
|
/// TODO: LeetCode 19 删除链表的倒数第 N 个节点
|
||||||
|
ListNode? deleteFromEnd(int n) {
|
||||||
|
ListNode? dummy = ListNode(0, this);
|
||||||
|
ListNode? first = this;
|
||||||
|
ListNode? second = dummy;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
first = first?.next;
|
||||||
|
}
|
||||||
|
while (first != null) {
|
||||||
|
first = first.next;
|
||||||
|
second = second?.next;
|
||||||
|
}
|
||||||
|
second?.next = second.next?.next;
|
||||||
|
return dummy.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListNode? deleteNode(ListNode node) {
|
||||||
|
ListNode? cur = this;
|
||||||
|
if (cur == node) {
|
||||||
|
cur = cur.next!;
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
while (cur != null) {
|
||||||
|
if (cur.next == node) {
|
||||||
|
cur.next = cur.next?.next;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
cur = cur.next;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
//// region [2023.11.23]
|
||||||
|
/// [2023.11.23]
|
||||||
|
/// TODO: 完成 getEvenValue 方法,
|
||||||
|
/// 返回链表中偶数节点的值列表
|
||||||
|
List<int> getEvenValue() {
|
||||||
|
ListNode? cur = this;
|
||||||
|
List<int> result = [];
|
||||||
|
|
||||||
|
while (cur != null) {
|
||||||
|
result.add(cur.val);
|
||||||
|
cur = cur.next?.next;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: 完成 getValueByStep 方法,
|
||||||
|
/// 返回链表中每隔 step 个节点的值列表
|
||||||
|
/// 例:
|
||||||
|
/// 链表: 0->3->2->8->9
|
||||||
|
/// step=3 : 输出 [0, 8]
|
||||||
|
/// step=2 : 输出 [0, 2, 9]
|
||||||
|
List<int> getValueByStep(int step) {
|
||||||
|
ListNode? cur = this;
|
||||||
|
List<int> result = [];
|
||||||
|
|
||||||
|
while (cur != null) {
|
||||||
|
result.add(cur.val);
|
||||||
|
for (int i = 0; i < step; i++) {
|
||||||
|
cur = cur?.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [2023.11.23]
|
||||||
|
/// TODO: LeetCode 876 链表的中间结点
|
||||||
|
/// [1,2,3,4,5] -> [3,4,5]
|
||||||
|
/// [1,2,3,4,5,6] -> [4,5,6]
|
||||||
|
/// 时间 O(N) 空间 O(N)
|
||||||
|
ListNode? middleNode1() {
|
||||||
|
List<ListNode?> nodes = [];
|
||||||
|
ListNode? cur = this;
|
||||||
|
while (cur != null) {
|
||||||
|
nodes.add(cur);
|
||||||
|
cur = cur.next;
|
||||||
|
}
|
||||||
|
return nodes[nodes.length ~/ 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 使用快慢指针 - 一次遍历
|
||||||
|
ListNode? middleNode2() {
|
||||||
|
ListNode? slow = this;
|
||||||
|
ListNode? fast = this;
|
||||||
|
while (fast != null && fast.next != null) {
|
||||||
|
slow = slow?.next;
|
||||||
|
fast = fast.next?.next;
|
||||||
|
}
|
||||||
|
return slow;
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
3
test/structure/link_list/linked_list.dart
Normal file
3
test/structure/link_list/linked_list.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
class LikedList<T>{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,7 +23,4 @@ class ListNode {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user