0

I want to extend Darts map with an abstract class for a database table which then must be extended for each table (see full example below). The abstract class has a constructor which already sets some entries, but does not explicitly invoke any constructor in its super class.

Problem is to create the subclass and directly set entries (like map allows).

I have tried to correctly follow this answer.

import 'package:uuid/uuid.dart';

void main(List<String> args) {
  Map<String, dynamic> map = {
    'key': 99,
  };
  SomeTable one = SomeTable();
  print('$one with ${one.length} entries -> ${one.prettyPrint()}');
  SomeTable another = SomeTable();
  one['child'] = another;
  print('$one with ${one.length} entries -> ${one.prettyPrint()}');
  SomeTable third = {
    'key': 17,
  };
}

class SomeTable extends TableMap {
  SomeTable() : super('sometable');
  void someSpecificFunction() {}
}

abstract class TableMap implements Map<String, dynamic> {
  final Map _inner = <String, dynamic>{};

  static const String id = 'id', // 64bit
      uuid = 'uuid',
      identifier = 'identifier', // String "$tablename:$id"
      tablename = 'tablename';

  /// Constructor
  TableMap(String _tablename) {
    String uuidAsString = Uuid().v4();
    _inner[uuid] = uuidAsString;
    _inner[identifier] = tablename + ':' + uuidAsString;
    _inner[tablename] = _tablename;
  }

  /// Returns `true` if `ìd` = `null`
  bool get isNew => ([id] == null) ? true : false;

  @override
  operator [](Object key) {
    return _inner[key];
  }

  @override
  void operator []=(String key, value) {
    _inner[key] = value;
  }

  @override
  void addAll(Map<String, dynamic> other) {
    _inner.addAll(other);
  }

  @override
  void addEntries(Iterable<MapEntry<String, dynamic>> newEntries) {
    _inner.addEntries(newEntries);
  }

  @override
  Map<RK, RV> cast<RK, RV>() {
    return _inner.cast<RK, RV>();
  }

  @override
  void clear() {
    _inner.clear();
  }

  @override
  bool containsKey(Object key) {
    return _inner.containsKey(key);
  }

  @override
  bool containsValue(Object value) {
    return _inner.containsValue(value);
  }

  @override
  Iterable<MapEntry<String, dynamic>> get entries => _inner.entries;

  @override
  void forEach(void Function(String key, dynamic value) f) {
    _inner.forEach(f);
  }

  @override
  bool get isEmpty => _inner.isEmpty;

  @override
  bool get isNotEmpty => _inner.isNotEmpty;

  @override
  Iterable<String> get keys => _inner.keys;

  @override
  int get length => _inner.length;

  @override
  Map<K2, V2> map<K2, V2>(
      MapEntry<K2, V2> Function(String key, dynamic value) f) {
    return _inner.map(f);
  }

  @override
  putIfAbsent(String key, Function() ifAbsent) {
    return _inner.putIfAbsent(key, ifAbsent);
  }

  @override
  remove(Object key) {
    return _inner.remove(key);
  }

  @override
  void removeWhere(bool Function(String key, dynamic value) predicate) {
    _inner.removeWhere(predicate);
  }

  @override
  update(String key, Function(dynamic value) update, {Function() ifAbsent}) {
    return _inner.update(key, update, ifAbsent: ifAbsent);
  }

  @override
  void updateAll(Function(String key, dynamic value) update) {
    _inner.updateAll(update);
  }

  @override
  Iterable get values => _inner.values;

  /// Creates a string by putting each entry on a separate line and
  /// prefixing it with spaces according to its depth within the map.
  String prettyPrint() {
    return _prettyPrintMap(this, 0);
  }

  static final String _spaces = '                                   ';

  String _prettyPrintList(List<dynamic> list, int depth) {
    depth++;
    String _indent = _spaces.substring(0, 2 * depth);
    String out = '[\n';
    list.forEach((element) {
      out = out + _indent;
      print('_printList( element=$element is ${element.runtimeType}');
      if (element is Map) {
        out = out + _prettyPrintMap(element, depth) + ',\n';
      } else if (element is List) {
        out = out + _prettyPrintList(element, depth) + ',\n';
      } else {
        out = out + element.toString() + ',\n';
      }
    });
    depth--;
    return out + ']';
  }

  String _prettyPrintMap(Map<dynamic, dynamic> map, int depth) {
    depth++;
    String _indent = _spaces.substring(0, 2 * depth);
    String out = '{\n';
    map.forEach((k, v) {
      out = out + _indent + k + ': ';
      if (v is Map) {
        out = out + _prettyPrintMap(v, depth) + ',\n';
      } else if (v is List) {
        out = out + _prettyPrintList(v, depth);
      } else {
        out = out + v.toString() + ',\n';
      }
    });
    depth--;
    return out + '}';
  }
}
Jemolah
  • 1,962
  • 3
  • 24
  • 41

1 Answers1

0

Your TableMap class doesn't extend Map, it implements it. Hence you do not call the constructor of Map - which seems the correct approach.

If you want to fill your data-holding Map _inner at construction, you can do so in your constructor; the way you are doing it at the moment looks fine, or you can initialize your variable in the constructor altogether.

Of course you cannot just use the literal syntax for your custom implementation - the literal is basically bound to the default Map. But you can add a constructor that takes some values and passes those on into your private base class constructor, which then adds the values adds the values on _inner.

For example, you could add (similar to the Map interface):

factory SomeTable.fromEntries(Iterable<MapEntry> entries) {
  final table = SomeTable();
  table._inner.addEntries(entries);
}

and then use it:

SomeTable third = SomeTable.fromEntries({
  'key': 17,
});

Similarly, you could add a Map or an Iterable to the exisiting or new constructors of your TableMap class, and pass the data along during construction.

Eiko
  • 25,601
  • 15
  • 56
  • 71
  • Thanks for having a look into the problem. Unfortunately lines 12-14 do not work. I cannot fill data with any constructor. Could you please provide a code example? – Jemolah Jun 06 '20 at 11:22
  • Please see the added example. There are many ways to do it similar, of course. But I guess you will not be able to simply use the curly braces alone. – Eiko Jun 07 '20 at 07:49