<commit_before>// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of observe;

// This code is inspired by ChangeSummary:
// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
// ...which underlies MDV. Since we don't need the functionality of
// ChangeSummary, we just implement what we need for data bindings.
// This allows our implementation to be much simpler.

/**
 * A data-bound path starting from a view-model or model object, for example
 * `foo.bar.baz`.
 *
 * When the [values] stream is being listened to, this will observe changes to
 * the object and any intermediate object along the path, and send [values]
 * accordingly. When all listeners are unregistered it will stop observing
 * the objects.
 *
 * This class is used to implement [Node.bind] and similar functionality.
 */
class PathObserver extends ChangeNotifierBase {
  /** The path string. */
  final String path;

  /** True if the path is valid, otherwise false. */
  final bool _isValid;

  final List<Object> _segments;
  List<Object> _values;
  List<StreamSubscription> _subs;

  /**
   * Observes [path] on [object] for changes. This returns an object that can be
   * used to get the changes and get/set the value at this path.
   * See [PathObserver.bindSync] and [PathObserver.value].
   */
  PathObserver(Object object, String path)
      : path = path,
        _isValid = _isPathValid(path),
        _segments = <Object>[] {

    if (_isValid) {
      for (var segment in path.trim().split('.')) {
        if (segment == '') continue;
        var index = int.parse(segment, radix: 10, onError: (_) => null);
        _segments.add(index != null ? index : new Symbol(segment));
      }
    }

    // Initialize arrays.
    // Note that the path itself can't change after it is initially
    // constructed, even though the objects along the path can change.
    _values = new List<Object>(_segments.length + 1);
    _values[0] = object;
    _subs = new List<StreamSubscription>(_segments.length);
  }

  /** The object being observed. */
  get object => _values[0];

  /** Gets the last reported value at this path. */
  get value {
    if (!_isValid) return null;
    if (!hasObservers) _updateValues();
    return _values.last;
  }

  /** Sets the value at this path. */
  void set value(Object value) {
    int len = _segments.length;

    // TODO(jmesserly): throw if property cannot be set?
    // MDV seems tolerant of these errors.
    if (len == 0) return;
    if (!hasObservers) _updateValues();

    if (_setObjectProperty(_values[len - 1], _segments[len - 1], value)) {
      // Technically, this would get updated asynchronously via a change record.
      // However, it is nice if calling the getter will yield the same value
      // that was just set. So we use this opportunity to update our cache.
      _values[len] = value;
    }
  }

  /**
   * Invokes the [callback] immediately with the current [value], and every time
   * the value changes. This is useful for bindings, which want to be up-to-date
   * immediately and stay bound to the value of the path.
   */
  StreamSubscription bindSync(void callback(value)) {
    var result = changes.listen((records) { callback(value); });
    callback(value);
    return result;
  }

  void _observed() {
    super._observed();
    _updateValues();
    _observePath();
  }

  void _unobserved() {
    for (int i = 0; i < _subs.length; i++) {
      if (_subs[i] != null) {
        _subs[i].cancel();
        _subs[i] = null;
      }
    }
  }

  // TODO(jmesserly): should we be caching these values if not observing?
  void _updateValues() {
    for (int i = 0; i < _segments.length; i++) {
      _values[i + 1] = _getObjectProperty(_values[i], _segments[i]);
    }
  }

  void _updateObservedValues([int start = 0]) {
    bool changed = false;
    for (int i = start; i < _segments.length; i++) {
      final newValue = _getObjectProperty(_values[i], _segments[i]);
      if (identical(_values[i + 1], newValue)) {
        _observePath(start, i);
        return;
      }
      _values[i + 1] = newValue;
      changed = true;
    }

    _observePath(start);
    if (changed) {
      notifyChange(new PropertyChangeRecord(const Symbol('value')));
    }
  }

  void _observePath([int start = 0, int end]) {
    if (end == null) end = _segments.length;

    for (int i = start; i < end; i++) {
      if (_subs[i] != null) _subs[i].cancel();
      _observeIndex(i);
    }
  }

  void _observeIndex(int i) {
    final object = _values[i];
    if (object is Observable) {
      // TODO(jmesserly): rather than allocating a new closure for each
      // property, we could try and have one for the entire path. In that case,
      // we would lose information about which object changed (note: unless
      // PropertyChangeRecord is modified to includes the sender object), so
      // we would need to re-evaluate the entire path. Need to evaluate perf.
      _subs[i] = object.changes.listen((List<ChangeRecord> records) {
        if (!identical(_values[i], object)) {
          // Ignore this object if we're now tracking something else.
          return;
        }

        for (var record in records) {
          if (record.changes(_segments[i])) {
            _updateObservedValues(i);
            return;
          }
        }
      });
    }
  }
}

_getObjectProperty(object, property) {
  if (object is List && property is int) {
    if (property >= 0 && property < object.length) {
      return object[property];
    } else {
      return null;<commit_msg>Fixing first-chance exception in TodoMVC<commit_after>// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of observe;

// This code is inspired by ChangeSummary:
// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
// ...which underlies MDV. Since we don't need the functionality of
// ChangeSummary, we just implement what we need for data bindings.
// This allows our implementation to be much simpler.

/**
 * A data-bound path starting from a view-model or model object, for example
 * `foo.bar.baz`.
 *
 * When the [values] stream is being listened to, this will observe changes to
 * the object and any intermediate object along the path, and send [values]
 * accordingly. When all listeners are unregistered it will stop observing
 * the objects.
 *
 * This class is used to implement [Node.bind] and similar functionality.
 */
class PathObserver extends ChangeNotifierBase {
  /** The path string. */
  final String path;

  /** True if the path is valid, otherwise false. */
  final bool _isValid;

  final List<Object> _segments;
  List<Object> _values;
  List<StreamSubscription> _subs;

  /**
   * Observes [path] on [object] for changes. This returns an object that can be
   * used to get the changes and get/set the value at this path.
   * See [PathObserver.bindSync] and [PathObserver.value].
   */
  PathObserver(Object object, String path)
      : path = path,
        _isValid = _isPathValid(path),
        _segments = <Object>[] {

    if (_isValid) {
      for (var segment in path.trim().split('.')) {
        if (segment == '') continue;
        var index = int.parse(segment, radix: 10, onError: (_) => null);
        _segments.add(index != null ? index : new Symbol(segment));
      }
    }

    // Initialize arrays.
    // Note that the path itself can't change after it is initially
    // constructed, even though the objects along the path can change.
    _values = new List<Object>(_segments.length + 1);
    _values[0] = object;
    _subs = new List<StreamSubscription>(_segments.length);
  }

  /** The object being observed. */
  get object => _values[0];

  /** Gets the last reported value at this path. */
  get value {
    if (!_isValid) return null;
    if (!hasObservers) _updateValues();
    return _values.last;
  }

  /** Sets the value at this path. */
  void set value(Object value) {
    int len = _segments.length;

    // TODO(jmesserly): throw if property cannot be set?
    // MDV seems tolerant of these errors.
    if (len == 0) return;
    if (!hasObservers) _updateValues();

    if (_setObjectProperty(_values[len - 1], _segments[len - 1], value)) {
      // Technically, this would get updated asynchronously via a change record.
      // However, it is nice if calling the getter will yield the same value
      // that was just set. So we use this opportunity to update our cache.
      _values[len] = value;
    }
  }

  /**
   * Invokes the [callback] immediately with the current [value], and every time
   * the value changes. This is useful for bindings, which want to be up-to-date
   * immediately and stay bound to the value of the path.
   */
  StreamSubscription bindSync(void callback(value)) {
    var result = changes.listen((records) { callback(value); });
    callback(value);
    return result;
  }

  void _observed() {
    super._observed();
    _updateValues();
    _observePath();
  }

  void _unobserved() {
    for (int i = 0; i < _subs.length; i++) {
      if (_subs[i] != null) {
        _subs[i].cancel();
        _subs[i] = null;
      }
    }
  }

  // TODO(jmesserly): should we be caching these values if not observing?
  void _updateValues() {
    for (int i = 0; i < _segments.length; i++) {
      _values[i + 1] = _getObjectProperty(_values[i], _segments[i]);
    }
  }

  void _updateObservedValues([int start = 0]) {
    bool changed = false;
    for (int i = start; i < _segments.length; i++) {
      final newValue = _getObjectProperty(_values[i], _segments[i]);
      if (identical(_values[i + 1], newValue)) {
        _observePath(start, i);
        return;
      }
      _values[i + 1] = newValue;
      changed = true;
    }

    _observePath(start);
    if (changed) {
      notifyChange(new PropertyChangeRecord(const Symbol('value')));
    }
  }

  void _observePath([int start = 0, int end]) {
    if (end == null) end = _segments.length;

    for (int i = start; i < end; i++) {
      if (_subs[i] != null) _subs[i].cancel();
      _observeIndex(i);
    }
  }

  void _observeIndex(int i) {
    final object = _values[i];
    if (object is Observable) {
      // TODO(jmesserly): rather than allocating a new closure for each
      // property, we could try and have one for the entire path. In that case,
      // we would lose information about which object changed (note: unless
      // PropertyChangeRecord is modified to includes the sender object), so
      // we would need to re-evaluate the entire path. Need to evaluate perf.
      _subs[i] = object.changes.listen((List<ChangeRecord> records) {
        if (!identical(_values[i], object)) {
          // Ignore this object if we're now tracking something else.
          return;
        }

        for (var record in records) {
          if (record.changes(_segments[i])) {
            _updateObservedValues(i);
            return;
          }
        }
      });
    }
  }
}

_getObjectProperty(object, property) {
  if (object == null) {
    return null;
  }

  if (object is List && property is int) {
    if (property >= 0 && property < object.length) {
      return object[property];
    } else {
      return null;
{'fraction_non_alphanumeric': 0.11340475568330285, 'fraction_numerical': 0.003484017071683651, 'mean_word_length': 2.6931489224831133, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1431523', 'n_tokens_mistral': 3464, 'n_tokens_neox': 3367, 'n_words': 1236}