| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analyzer.src.context.context; | 5 library analyzer.src.context.context; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:analyzer/src/cancelable_future.dart'; | 10 import 'package:analyzer/src/cancelable_future.dart'; |
| (...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 } | 284 } |
| 285 | 285 |
| 286 @override | 286 @override |
| 287 List<Source> get htmlSources => _getSources(SourceKind.HTML); | 287 List<Source> get htmlSources => _getSources(SourceKind.HTML); |
| 288 | 288 |
| 289 @override | 289 @override |
| 290 bool get isDisposed => _disposed; | 290 bool get isDisposed => _disposed; |
| 291 | 291 |
| 292 @override | 292 @override |
| 293 List<Source> get launchableClientLibrarySources { | 293 List<Source> get launchableClientLibrarySources { |
| 294 List<Source> sources = new List<Source>(); | 294 List<Source> sources = <Source>[]; |
| 295 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 295 for (Source source in _cache.sources) { |
| 296 while (iterator.moveNext()) { | 296 CacheEntry entry = _cache.get(source); |
| 297 AnalysisTarget target = iterator.key; | 297 if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY && |
| 298 CacheEntry entry = iterator.value; | 298 !source.isInSystemLibrary && |
| 299 if (target is Source && | 299 isClientLibrary(source)) { |
| 300 entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY && | 300 sources.add(source); |
| 301 !target.isInSystemLibrary && | |
| 302 isClientLibrary(target)) { | |
| 303 sources.add(target); | |
| 304 } | 301 } |
| 305 } | 302 } |
| 306 return sources; | 303 return sources; |
| 307 } | 304 } |
| 308 | 305 |
| 309 @override | 306 @override |
| 310 List<Source> get launchableServerLibrarySources { | 307 List<Source> get launchableServerLibrarySources { |
| 311 List<Source> sources = new List<Source>(); | 308 List<Source> sources = <Source>[]; |
| 312 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 309 for (Source source in _cache.sources) { |
| 313 while (iterator.moveNext()) { | 310 CacheEntry entry = _cache.get(source); |
| 314 AnalysisTarget target = iterator.key; | 311 if (source is Source && |
| 315 CacheEntry entry = iterator.value; | |
| 316 if (target is Source && | |
| 317 entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY && | 312 entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY && |
| 318 !target.isInSystemLibrary && | 313 !source.isInSystemLibrary && |
| 319 isServerLibrary(target)) { | 314 isServerLibrary(source)) { |
| 320 sources.add(target); | 315 sources.add(source); |
| 321 } | 316 } |
| 322 } | 317 } |
| 323 return sources; | 318 return sources; |
| 324 } | 319 } |
| 325 | 320 |
| 326 @override | 321 @override |
| 327 List<Source> get librarySources => _getSources(SourceKind.LIBRARY); | 322 List<Source> get librarySources => _getSources(SourceKind.LIBRARY); |
| 328 | 323 |
| 329 @override | 324 @override |
| 330 Stream<SourcesChangedEvent> get onSourcesChanged => | 325 Stream<SourcesChangedEvent> get onSourcesChanged => |
| (...skipping 26 matching lines...) Expand all Loading... |
| 357 _sourceFactory.context = null; | 352 _sourceFactory.context = null; |
| 358 } | 353 } |
| 359 factory.context = this; | 354 factory.context = this; |
| 360 _sourceFactory = factory; | 355 _sourceFactory = factory; |
| 361 _cache = createCacheFromSourceFactory(factory); | 356 _cache = createCacheFromSourceFactory(factory); |
| 362 _invalidateAllLocalResolutionInformation(true); | 357 _invalidateAllLocalResolutionInformation(true); |
| 363 } | 358 } |
| 364 | 359 |
| 365 @override | 360 @override |
| 366 List<Source> get sources { | 361 List<Source> get sources { |
| 367 List<Source> sources = new List<Source>(); | 362 return _cache.sources.toList(); |
| 368 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | |
| 369 while (iterator.moveNext()) { | |
| 370 AnalysisTarget target = iterator.key; | |
| 371 if (target is Source) { | |
| 372 sources.add(target); | |
| 373 } | |
| 374 } | |
| 375 return sources; | |
| 376 } | 363 } |
| 377 | 364 |
| 378 /** | 365 /** |
| 379 * Return a list of the sources that would be processed by | 366 * Return a list of the sources that would be processed by |
| 380 * [performAnalysisTask]. This method duplicates, and must therefore be kept | 367 * [performAnalysisTask]. This method duplicates, and must therefore be kept |
| 381 * in sync with, [getNextAnalysisTask]. This method is intended to be used for | 368 * in sync with, [getNextAnalysisTask]. This method is intended to be used for |
| 382 * testing purposes only. | 369 * testing purposes only. |
| 383 */ | 370 */ |
| 384 List<Source> get sourcesNeedingProcessing { | 371 List<Source> get sourcesNeedingProcessing { |
| 385 HashSet<Source> sources = new HashSet<Source>(); | 372 HashSet<Source> sources = new HashSet<Source>(); |
| (...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 762 // } | 749 // } |
| 763 return null; | 750 return null; |
| 764 } | 751 } |
| 765 | 752 |
| 766 @override | 753 @override |
| 767 List<Source> getHtmlFilesReferencing(Source source) { | 754 List<Source> getHtmlFilesReferencing(Source source) { |
| 768 SourceKind sourceKind = getKindOf(source); | 755 SourceKind sourceKind = getKindOf(source); |
| 769 if (sourceKind == null) { | 756 if (sourceKind == null) { |
| 770 return Source.EMPTY_LIST; | 757 return Source.EMPTY_LIST; |
| 771 } | 758 } |
| 772 List<Source> htmlSources = new List<Source>(); | 759 List<Source> htmlSources = <Source>[]; |
| 773 while (true) { | 760 while (true) { |
| 774 if (sourceKind == SourceKind.PART) { | 761 if (sourceKind == SourceKind.PART) { |
| 775 List<Source> librarySources = getLibrariesContaining(source); | 762 List<Source> librarySources = getLibrariesContaining(source); |
| 776 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 763 for (Source source in _cache.sources) { |
| 777 while (iterator.moveNext()) { | 764 CacheEntry entry = _cache.get(source); |
| 778 CacheEntry entry = iterator.value; | |
| 779 if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) { | 765 if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) { |
| 780 List<Source> referencedLibraries = | 766 List<Source> referencedLibraries = |
| 781 (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES); | 767 (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES); |
| 782 if (_containsAny(referencedLibraries, librarySources)) { | 768 if (_containsAny(referencedLibraries, librarySources)) { |
| 783 htmlSources.add(iterator.key); | 769 htmlSources.add(source); |
| 784 } | 770 } |
| 785 } | 771 } |
| 786 } | 772 } |
| 787 } else { | 773 } else { |
| 788 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 774 for (Source source in _cache.sources) { |
| 789 while (iterator.moveNext()) { | 775 CacheEntry entry = _cache.get(source); |
| 790 CacheEntry entry = iterator.value; | |
| 791 if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) { | 776 if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) { |
| 792 List<Source> referencedLibraries = | 777 List<Source> referencedLibraries = |
| 793 (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES); | 778 (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES); |
| 794 if (_contains(referencedLibraries, source)) { | 779 if (_contains(referencedLibraries, source)) { |
| 795 htmlSources.add(iterator.key); | 780 htmlSources.add(source); |
| 796 } | 781 } |
| 797 } | 782 } |
| 798 } | 783 } |
| 799 } | 784 } |
| 800 break; | 785 break; |
| 801 } | 786 } |
| 802 if (htmlSources.isEmpty) { | 787 if (htmlSources.isEmpty) { |
| 803 return Source.EMPTY_LIST; | 788 return Source.EMPTY_LIST; |
| 804 } | 789 } |
| 805 return htmlSources; | 790 return htmlSources; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 816 return SourceKind.UNKNOWN; | 801 return SourceKind.UNKNOWN; |
| 817 } | 802 } |
| 818 | 803 |
| 819 @override | 804 @override |
| 820 List<Source> getLibrariesContaining(Source source) { | 805 List<Source> getLibrariesContaining(Source source) { |
| 821 SourceKind kind = getKindOf(source); | 806 SourceKind kind = getKindOf(source); |
| 822 if (kind == SourceKind.LIBRARY) { | 807 if (kind == SourceKind.LIBRARY) { |
| 823 return <Source>[source]; | 808 return <Source>[source]; |
| 824 } else if (kind == SourceKind.PART) { | 809 } else if (kind == SourceKind.PART) { |
| 825 List<Source> libraries = <Source>[]; | 810 List<Source> libraries = <Source>[]; |
| 826 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 811 for (Source library in _cache.sources) { |
| 827 while (iterator.moveNext()) { | 812 CacheEntry entry = _cache.get(library); |
| 828 AnalysisTarget target = iterator.key; | 813 if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) { |
| 829 if (target is Source && getKindOf(target) == SourceKind.LIBRARY) { | 814 List<Source> parts = entry.getValue(INCLUDED_PARTS); |
| 830 List<Source> parts = _cache.getValue(target, INCLUDED_PARTS); | |
| 831 if (parts.contains(source)) { | 815 if (parts.contains(source)) { |
| 832 libraries.add(target); | 816 libraries.add(library); |
| 833 } | 817 } |
| 834 } | 818 } |
| 835 } | 819 } |
| 836 if (libraries.isNotEmpty) { | 820 if (libraries.isNotEmpty) { |
| 837 return libraries; | 821 return libraries; |
| 838 } | 822 } |
| 839 } | 823 } |
| 840 return Source.EMPTY_ARRAY; | 824 return Source.EMPTY_ARRAY; |
| 841 } | 825 } |
| 842 | 826 |
| 843 @override | 827 @override |
| 844 List<Source> getLibrariesDependingOn(Source librarySource) { | 828 List<Source> getLibrariesDependingOn(Source librarySource) { |
| 845 List<Source> dependentLibraries = new List<Source>(); | 829 List<Source> dependentLibraries = <Source>[]; |
| 846 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 830 for (Source source in _cache.sources) { |
| 847 while (iterator.moveNext()) { | 831 CacheEntry entry = _cache.get(source); |
| 848 CacheEntry entry = iterator.value; | |
| 849 if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) { | 832 if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) { |
| 850 if (_contains(entry.getValue(EXPORTED_LIBRARIES), librarySource)) { | 833 if (_contains(entry.getValue(EXPORTED_LIBRARIES), librarySource)) { |
| 851 dependentLibraries.add(iterator.key); | 834 dependentLibraries.add(source); |
| 852 } | 835 } |
| 853 if (_contains(entry.getValue(IMPORTED_LIBRARIES), librarySource)) { | 836 if (_contains(entry.getValue(IMPORTED_LIBRARIES), librarySource)) { |
| 854 dependentLibraries.add(iterator.key); | 837 dependentLibraries.add(source); |
| 855 } | 838 } |
| 856 } | 839 } |
| 857 } | 840 } |
| 858 if (dependentLibraries.isEmpty) { | 841 if (dependentLibraries.isEmpty) { |
| 859 return Source.EMPTY_LIST; | 842 return Source.EMPTY_LIST; |
| 860 } | 843 } |
| 861 return dependentLibraries; | 844 return dependentLibraries; |
| 862 } | 845 } |
| 863 | 846 |
| 864 @override | 847 @override |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 946 // if (sourceEntry is HtmlEntry) { | 929 // if (sourceEntry is HtmlEntry) { |
| 947 // HtmlEntry htmlEntry = sourceEntry; | 930 // HtmlEntry htmlEntry = sourceEntry; |
| 948 // return htmlEntry.getValue(HtmlEntry.RESOLVED_UNIT); | 931 // return htmlEntry.getValue(HtmlEntry.RESOLVED_UNIT); |
| 949 // } | 932 // } |
| 950 return null; | 933 return null; |
| 951 } | 934 } |
| 952 | 935 |
| 953 @override | 936 @override |
| 954 List<Source> getSourcesWithFullName(String path) { | 937 List<Source> getSourcesWithFullName(String path) { |
| 955 List<Source> sources = <Source>[]; | 938 List<Source> sources = <Source>[]; |
| 956 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 939 for (Source source in _cache.sources) { |
| 957 while (iterator.moveNext()) { | 940 if (source.fullName == path) { |
| 958 AnalysisTarget target = iterator.key; | 941 sources.add(source); |
| 959 if (target is Source && target.fullName == path) { | |
| 960 sources.add(target); | |
| 961 } | 942 } |
| 962 } | 943 } |
| 963 return sources; | 944 return sources; |
| 964 } | 945 } |
| 965 | 946 |
| 966 @override | 947 @override |
| 967 bool handleContentsChanged( | 948 bool handleContentsChanged( |
| 968 Source source, String originalContents, String newContents, bool notify) { | 949 Source source, String originalContents, String newContents, bool notify) { |
| 969 CacheEntry entry = _cache.get(source); | 950 CacheEntry entry = _cache.get(source); |
| 970 if (entry == null) { | 951 if (entry == null) { |
| (...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1243 */ | 1224 */ |
| 1244 void visitContentCache(ContentCacheVisitor visitor) { | 1225 void visitContentCache(ContentCacheVisitor visitor) { |
| 1245 _contentCache.accept(visitor); | 1226 _contentCache.accept(visitor); |
| 1246 } | 1227 } |
| 1247 | 1228 |
| 1248 /** | 1229 /** |
| 1249 * Add all of the sources contained in the given source [container] to the | 1230 * Add all of the sources contained in the given source [container] to the |
| 1250 * given list of [sources]. | 1231 * given list of [sources]. |
| 1251 */ | 1232 */ |
| 1252 void _addSourcesInContainer(List<Source> sources, SourceContainer container) { | 1233 void _addSourcesInContainer(List<Source> sources, SourceContainer container) { |
| 1253 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 1234 for (Source source in _cache.sources) { |
| 1254 while (iterator.moveNext()) { | |
| 1255 Source source = iterator.key; | |
| 1256 if (container.contains(source)) { | 1235 if (container.contains(source)) { |
| 1257 sources.add(source); | 1236 sources.add(source); |
| 1258 } | 1237 } |
| 1259 } | 1238 } |
| 1260 } | 1239 } |
| 1261 | 1240 |
| 1262 /** | 1241 /** |
| 1263 * Remove the given [pendingFuture] from [_pendingFutureTargets], since the | 1242 * Remove the given [pendingFuture] from [_pendingFutureTargets], since the |
| 1264 * client has indicated its computation is not needed anymore. | 1243 * client has indicated its computation is not needed anymore. |
| 1265 */ | 1244 */ |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1413 List<ChangeNotice> notices = new List.from(_pendingNotices.values); | 1392 List<ChangeNotice> notices = new List.from(_pendingNotices.values); |
| 1414 _pendingNotices.clear(); | 1393 _pendingNotices.clear(); |
| 1415 return notices; | 1394 return notices; |
| 1416 } | 1395 } |
| 1417 | 1396 |
| 1418 /** | 1397 /** |
| 1419 * Return a list containing all of the sources known to this context that have | 1398 * Return a list containing all of the sources known to this context that have |
| 1420 * the given [kind]. | 1399 * the given [kind]. |
| 1421 */ | 1400 */ |
| 1422 List<Source> _getSources(SourceKind kind) { | 1401 List<Source> _getSources(SourceKind kind) { |
| 1423 List<Source> sources = new List<Source>(); | 1402 List<Source> sources = <Source>[]; |
| 1424 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 1403 for (Source source in _cache.sources) { |
| 1425 while (iterator.moveNext()) { | 1404 CacheEntry entry = _cache.get(source); |
| 1426 if (iterator.value.getValue(SOURCE_KIND) == kind && | 1405 if (entry.getValue(SOURCE_KIND) == kind) { |
| 1427 iterator.key is Source) { | 1406 sources.add(source); |
| 1428 sources.add(iterator.key); | |
| 1429 } | 1407 } |
| 1430 } | 1408 } |
| 1431 return sources; | 1409 return sources; |
| 1432 } | 1410 } |
| 1433 | 1411 |
| 1434 /** | 1412 /** |
| 1435 * Look at the given [source] to see whether a task needs to be performed | 1413 * Look at the given [source] to see whether a task needs to be performed |
| 1436 * related to it. If so, add the source to the set of sources that need to be | 1414 * related to it. If so, add the source to the set of sources that need to be |
| 1437 * processed. This method is intended to be used for testing purposes only. | 1415 * processed. This method is intended to be used for testing purposes only. |
| 1438 */ | 1416 */ |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1617 // } | 1595 // } |
| 1618 // } | 1596 // } |
| 1619 // }); | 1597 // }); |
| 1620 } | 1598 } |
| 1621 | 1599 |
| 1622 /** | 1600 /** |
| 1623 * Remove the given [source] from the priority order if it is in the list. | 1601 * Remove the given [source] from the priority order if it is in the list. |
| 1624 */ | 1602 */ |
| 1625 void _removeFromPriorityOrder(Source source) { | 1603 void _removeFromPriorityOrder(Source source) { |
| 1626 int count = _priorityOrder.length; | 1604 int count = _priorityOrder.length; |
| 1627 List<Source> newOrder = new List<Source>(); | 1605 List<Source> newOrder = <Source>[]; |
| 1628 for (int i = 0; i < count; i++) { | 1606 for (int i = 0; i < count; i++) { |
| 1629 if (_priorityOrder[i] != source) { | 1607 if (_priorityOrder[i] != source) { |
| 1630 newOrder.add(_priorityOrder[i]); | 1608 newOrder.add(_priorityOrder[i]); |
| 1631 } | 1609 } |
| 1632 } | 1610 } |
| 1633 if (newOrder.length < count) { | 1611 if (newOrder.length < count) { |
| 1634 analysisPriorityOrder = newOrder; | 1612 analysisPriorityOrder = newOrder; |
| 1635 } | 1613 } |
| 1636 } | 1614 } |
| 1637 | 1615 |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1801 /** | 1779 /** |
| 1802 * Check the cache for any invalid entries (entries whose modification time | 1780 * Check the cache for any invalid entries (entries whose modification time |
| 1803 * does not match the modification time of the source associated with the | 1781 * does not match the modification time of the source associated with the |
| 1804 * entry). Invalid entries will be marked as invalid so that the source will | 1782 * entry). Invalid entries will be marked as invalid so that the source will |
| 1805 * be re-analyzed. Return `true` if at least one entry was invalid. | 1783 * be re-analyzed. Return `true` if at least one entry was invalid. |
| 1806 */ | 1784 */ |
| 1807 bool _validateCacheConsistency() { | 1785 bool _validateCacheConsistency() { |
| 1808 int consistencyCheckStart = JavaSystem.nanoTime(); | 1786 int consistencyCheckStart = JavaSystem.nanoTime(); |
| 1809 HashSet<Source> changedSources = new HashSet<Source>(); | 1787 HashSet<Source> changedSources = new HashSet<Source>(); |
| 1810 HashSet<Source> missingSources = new HashSet<Source>(); | 1788 HashSet<Source> missingSources = new HashSet<Source>(); |
| 1811 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | 1789 for (Source source in _cache.sources) { |
| 1812 while (iterator.moveNext()) { | 1790 CacheEntry entry = _cache.get(source); |
| 1813 AnalysisTarget target = iterator.key; | 1791 int sourceTime = getModificationStamp(source); |
| 1814 if (target is Source) { | 1792 if (sourceTime != entry.modificationTime) { |
| 1815 CacheEntry entry = iterator.value; | 1793 changedSources.add(source); |
| 1816 int sourceTime = getModificationStamp(target); | 1794 } |
| 1817 if (sourceTime != entry.modificationTime) { | 1795 if (entry.exception != null) { |
| 1818 changedSources.add(target); | 1796 if (!exists(source)) { |
| 1819 } | 1797 missingSources.add(source); |
| 1820 if (entry.exception != null) { | |
| 1821 if (!exists(target)) { | |
| 1822 missingSources.add(target); | |
| 1823 } | |
| 1824 } | 1798 } |
| 1825 } | 1799 } |
| 1826 } | 1800 } |
| 1827 for (Source source in changedSources) { | 1801 for (Source source in changedSources) { |
| 1828 _sourceChanged(source); | 1802 _sourceChanged(source); |
| 1829 } | 1803 } |
| 1830 int removalCount = 0; | 1804 int removalCount = 0; |
| 1831 for (Source source in missingSources) { | 1805 for (Source source in missingSources) { |
| 1832 if (getLibrariesContaining(source).isEmpty && | 1806 if (getLibrariesContaining(source).isEmpty && |
| 1833 getLibrariesDependingOn(source).isEmpty) { | 1807 getLibrariesDependingOn(source).isEmpty) { |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2038 PendingFuture pendingFuture = | 2012 PendingFuture pendingFuture = |
| 2039 new PendingFuture<T>(_context, target, computeValue); | 2013 new PendingFuture<T>(_context, target, computeValue); |
| 2040 if (!pendingFuture.evaluate(entry)) { | 2014 if (!pendingFuture.evaluate(entry)) { |
| 2041 _context._pendingFutureTargets | 2015 _context._pendingFutureTargets |
| 2042 .putIfAbsent(target, () => <PendingFuture>[]) | 2016 .putIfAbsent(target, () => <PendingFuture>[]) |
| 2043 .add(pendingFuture); | 2017 .add(pendingFuture); |
| 2044 } | 2018 } |
| 2045 return pendingFuture.future; | 2019 return pendingFuture.future; |
| 2046 } | 2020 } |
| 2047 } | 2021 } |
| OLD | NEW |