Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: TsxProviderBase with test function #72

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
72 changes: 65 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,23 @@ Load a TMX file into a string by any means, and then pass the string to TileMapP

```dart
final String tmxBody = /* ... */;
final TiledMap mapTmx = TileMapParser.parseTmx(tmxBody);
final TiledMap mapTmx = TiledMap.parseTmx(tmxBody);
```

If your tmx file includes a external tsx reference, you have to add a CustomParser
If your tmx file includes external reference, e.g. for .tsx files, you have to add providers.
These providers are then used to find these files and load their contents as a string,
as well as maybe caching them.

To construct a provider for tsx files for example you can just extend the Provider<Parser>
or ParserProvider class, which is just a type alias.
```dart
class CustomTsxProvider extends TsxProvider {
class MultipleTsxProvider extends ParserProvider {
@override
bool canProvide(String filename) => ["external1.tsx", "external2.tsx"].contains(filename);

@override
Parser? getCachedSource(String filename) => null;

@override
Parser getSource(String fileName) {
final xml = File(fileName).readAsStringSync();
Expand All @@ -40,18 +51,65 @@ class CustomTsxProvider extends TsxProvider {
}
}
```
And use it in the parseTmx method

If, for example, all your tsx files are in one directory,
adding only one RelativeTsxProvider can allow the Parser to find all of them.

```dart
final String tmxBody = /* ... */;
final TiledMap mapTmx = TileMapParser.parseTmx(tmxBody, tsx: CustomTsxProvider());
class RelativeTsxProvider extends TsxProviderBase {
final String root;

RelativeTsxProvider(this.root);

@override
bool canProvide(String filename) {
if (cache.containsKey(filename)) return true;

final exists = File(paths.join(root, filename)).existsSync();
if (exists) cache[filename] = null;

return exists;
}

Map<String, Parser?> cache = {};

@override
Parser? getCachedSource(String filename) => cache[filename];

@override
Parser getSource(String filename) {
final xml = XmlDocument.parse(File(paths.join(root, filename)).readAsStringSync());
final element = xml.getElement("tileset");
if (element == null) {
throw ParsingException(
"tileset",
null,
"This tsx file does not seem to contain a top-level tileset tag",
);
}

cache[filename] = XmlParser(element);
return cache[filename]!;
}
}
```

These providers are passed to the parseTmx or parseJson method.
Keep in mind that the first Provider that can provide a source is used!

```dart
final String tmxBody = /* ... */;
final TiledMap mapTmx = TiledMap.parseTmx(
tmxBody,
tsxProviders: [SingleTsxProvider(), MultipleTsxProvider()],
);
```

### Load Json Files
Alternatively load a json file.
```dart
final String jsonBody = /* ... */;
final TiledMap mapTmx = TileMapParser.parseJson(jsonBody);
final TiledMap mapTmx = TiledMap.parseJson(jsonBody);
```

### Implementation
Expand Down
7 changes: 5 additions & 2 deletions packages/tiled/lib/src/layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ abstract class Layer {
this.properties = CustomProperties.empty,
});

static Layer parse(Parser parser) {
static Layer parse(Parser parser, {List<ParserProvider>? templateProviders}) {
final type = parser.formatSpecificParsing(
(json) => json.getLayerType('type'),
(xml) => LayerTypeExtension.parseFromTmx(xml.element.name.toString()),
Expand Down Expand Up @@ -277,7 +277,10 @@ abstract class Layer {

static List<Layer> parseLayers(Parser parser) {
return parser.formatSpecificParsing(
(json) => json.getChildrenAs('layers', Layer.parse),
(json) => json.getChildrenAs(
'layers',
Layer.parse,
),
(xml) {
// It's very important not change the order of the layers
// during parsing!
Expand Down
21 changes: 20 additions & 1 deletion packages/tiled/lib/src/objects/tiled_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ class TiledObject {
bool point;
bool rectangle;

String? templatePath;
Template? template;

Text? text;
bool visible;

Expand All @@ -84,6 +86,7 @@ class TiledObject {
this.ellipse = false,
this.point = false,
this.rectangle = false,
this.templatePath,
this.template,
this.text,
this.visible = true,
Expand All @@ -93,9 +96,13 @@ class TiledObject {
});

bool get isPolyline => polyline.isNotEmpty;

bool get isPolygon => polygon.isNotEmpty;

bool get isPoint => point;

bool get isEllipse => ellipse;

bool get isRectangle => rectangle;

factory TiledObject.parse(Parser parser) {
Expand Down Expand Up @@ -126,7 +133,18 @@ class TiledObject {
(xml) => xml.getChildren('point').isNotEmpty,
);
final text = parser.getSingleChildOrNullAs('text', Text.parse);
final template = parser.getSingleChildOrNullAs('template', Template.parse);
final templatePath = parser.getStringOrNull('template');
final templateProvider = templatePath == null
? null
: parser.templateProviders
?.firstWhere((e) => e.canProvide(templatePath));
final template = templateProvider == null
? null
: Template.parse(
templateProvider.getCachedSource(templatePath!) ??
templateProvider.getSource(templatePath),
);

final properties = parser.getProperties();

final polygon = parsePointList(parser, 'polygon');
Expand All @@ -147,6 +165,7 @@ class TiledObject {
ellipse: ellipse,
point: point,
rectangle: rectangle,
templatePath: templatePath,
template: template,
text: text,
visible: visible,
Expand Down
50 changes: 45 additions & 5 deletions packages/tiled/lib/src/parser.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import 'dart:convert';

import 'package:tiled/tiled.dart';
import 'package:xml/xml.dart';

class ParsingException implements Exception {
final String name;
final String? valueFound;
final String reason;

ParsingException(this.name, this.valueFound, this.reason);
}

class XmlParser extends Parser {
final XmlElement element;
XmlParser(this.element);

XmlParser(this.element, {super.tsxProviders, super.templateProviders});

XmlParser.fromString(
String string, {
super.tsxProviders,
super.templateProviders,
}) : element = XmlDocument.parse(string).rootElement;

@override
String? getInnerTextOrNull() =>
Expand All @@ -26,15 +36,27 @@ class XmlParser extends Parser {
return element.children
.whereType<XmlElement>()
.where((e) => e.name.local == name)
.map(XmlParser.new)
.map(
(e) => XmlParser(
e,
templateProviders: templateProviders,
tsxProviders: tsxProviders,
),
)
.toList();
}

List<Parser> getChildrenWithNames(Set<String> names) {
return element.children
.whereType<XmlElement>()
.where((e) => names.contains(e.name.local))
.map(XmlParser.new)
.map(
(e) => XmlParser(
e,
tsxProviders: tsxProviders,
templateProviders: templateProviders,
),
)
.toList();
}

Expand All @@ -49,7 +71,14 @@ class XmlParser extends Parser {

class JsonParser extends Parser {
final Map<String, dynamic> json;
JsonParser(this.json);

JsonParser(this.json, {super.tsxProviders, super.templateProviders});

JsonParser.fromString(
String string, {
super.tsxProviders,
super.templateProviders,
}) : json = jsonDecode(string) as Map<String, dynamic>;

@override
String? getInnerTextOrNull() => null;
Expand All @@ -65,7 +94,13 @@ class JsonParser extends Parser {
return [];
}
return (json[name] as List<dynamic>)
.map((dynamic e) => JsonParser(e as Map<String, dynamic>))
.map(
(dynamic e) => JsonParser(
e as Map<String, dynamic>,
templateProviders: templateProviders,
tsxProviders: tsxProviders,
),
)
.toList();
}

Expand All @@ -83,6 +118,11 @@ class JsonParser extends Parser {
}

abstract class Parser {
final List<ParserProvider>? templateProviders;
final List<ParserProvider>? tsxProviders;

Parser({this.tsxProviders, this.templateProviders});

String? getInnerTextOrNull();

String? getStringOrNull(String name, {String? defaults});
Expand Down
11 changes: 11 additions & 0 deletions packages/tiled/lib/src/provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:tiled/tiled.dart';

abstract class Provider<T> {
bool canProvide(String path);

T getSource(String path);
T? getCachedSource(String path);
}

typedef ParserProvider = Provider<Parser>;
typedef ImagePathProvider = Provider<String>;
24 changes: 0 additions & 24 deletions packages/tiled/lib/src/tile_map_parser.dart

This file was deleted.

Loading
Loading