diff options
Diffstat (limited to 'MediaBrowser.Controller')
| -rw-r--r-- | MediaBrowser.Controller/Dto/DtoBuilder.cs | 22 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/BaseItem.cs | 43 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Folder.cs | 65 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Movies/Movie.cs | 16 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Video.cs | 12 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Library/ILibraryManager.cs | 21 | ||||
| -rw-r--r-- | MediaBrowser.Controller/MediaBrowser.Controller.csproj | 1 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Persistence/IItemRepository.cs | 74 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Persistence/IUserDataRepository.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Reflection/TypeMapper.cs | 47 |
10 files changed, 190 insertions, 113 deletions
diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs index 7fd188acb5..1e0e5286fa 100644 --- a/MediaBrowser.Controller/Dto/DtoBuilder.cs +++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs @@ -73,11 +73,6 @@ namespace MediaBrowser.Controller.Dto tasks.Add(AttachPeople(dto, item)); } - if (user != null) - { - tasks.Add(AttachUserSpecificInfo(dto, item, user, fields)); - } - if (fields.Contains(ItemFields.PrimaryImageAspectRatio)) { try @@ -91,6 +86,11 @@ namespace MediaBrowser.Controller.Dto } } + if (user != null) + { + AttachUserSpecificInfo(dto, item, user, fields); + } + AttachBasicFields(dto, item, fields); // Make sure all the tasks we kicked off have completed. @@ -109,7 +109,7 @@ namespace MediaBrowser.Controller.Dto /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <param name="fields">The fields.</param> - private async Task AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, List<ItemFields> fields) + private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, List<ItemFields> fields) { if (item.IsFolder && fields.Contains(ItemFields.DisplayPreferencesId)) { @@ -127,13 +127,13 @@ namespace MediaBrowser.Controller.Dto // Skip sorting since all we want is a count dto.ChildCount = folder.GetChildren(user).Count(); - await SetSpecialCounts(folder, user, dto, _userDataRepository).ConfigureAwait(false); + SetSpecialCounts(folder, user, dto, _userDataRepository); } } if (addUserData) { - var userData = await _userDataRepository.GetUserData(user.Id, item.GetUserDataKey()).ConfigureAwait(false); + var userData = _userDataRepository.GetUserData(user.Id, item.GetUserDataKey()); dto.UserData = GetUserItemDataDto(userData); @@ -529,7 +529,7 @@ namespace MediaBrowser.Controller.Dto /// <param name="dto">The dto.</param> /// <param name="userDataRepository">The user data repository.</param> /// <returns>Task.</returns> - private static async Task SetSpecialCounts(Folder folder, User user, BaseItemDto dto, IUserDataRepository userDataRepository) + private static void SetSpecialCounts(Folder folder, User user, BaseItemDto dto, IUserDataRepository userDataRepository) { var rcentlyAddedItemCount = 0; var recursiveItemCount = 0; @@ -540,7 +540,7 @@ namespace MediaBrowser.Controller.Dto // Loop through each recursive child foreach (var child in folder.GetRecursiveChildren(user).Where(i => !i.IsFolder).ToList()) { - var userdata = await userDataRepository.GetUserData(user.Id, child.GetUserDataKey()).ConfigureAwait(false); + var userdata = userDataRepository.GetUserData(user.Id, child.GetUserDataKey()); recursiveItemCount++; @@ -767,7 +767,7 @@ namespace MediaBrowser.Controller.Dto { if (data == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException("data"); } return new UserItemDataDto diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6ae465aa7e..2850d7092a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -273,7 +273,7 @@ namespace MediaBrowser.Controller.Entities { return Guid.Empty; } - + try { if (!ResolveArgs.IsDirectory) @@ -681,11 +681,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>List{Video}.</returns> private IEnumerable<Trailer> LoadLocalTrailers() { - if (LocationType != LocationType.FileSystem) - { - return new List<Trailer>(); - } - ItemResolveArgs resolveArgs; try @@ -737,7 +732,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths<Trailer>(files, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(video.Id) as Trailer; + var dbItem = LibraryManager.RetrieveItem(video.Id, typeof(Trailer)) as Trailer; if (dbItem != null) { @@ -756,11 +751,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>List{Audio.Audio}.</returns> private IEnumerable<Audio.Audio> LoadThemeSongs() { - if (LocationType != LocationType.FileSystem) - { - return new List<Audio.Audio>(); - } - ItemResolveArgs resolveArgs; try @@ -803,7 +793,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths<Audio.Audio>(files, null).Select(audio => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(audio.Id) as Audio.Audio; + var dbItem = LibraryManager.RetrieveItem(audio.Id, typeof(Audio.Audio)) as Audio.Audio; if (dbItem != null) { @@ -821,11 +811,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>List{Video}.</returns> private IEnumerable<Video> LoadThemeVideos() { - if (LocationType != LocationType.FileSystem) - { - return new List<Video>(); - } - ItemResolveArgs resolveArgs; try @@ -866,7 +851,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths<Video>(files, null).Select(item => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(item.Id) as Video; + var dbItem = LibraryManager.RetrieveItem(item.Id, typeof(Video)) as Video; if (dbItem != null) { @@ -896,13 +881,20 @@ namespace MediaBrowser.Controller.Entities cancellationToken.ThrowIfCancellationRequested(); - var themeSongsChanged = await RefreshThemeSongs(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var themeSongsChanged = false; - var themeVideosChanged = await RefreshThemeVideos(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var themeVideosChanged = false; - var localTrailersChanged = await RefreshLocalTrailers(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var localTrailersChanged = false; - cancellationToken.ThrowIfCancellationRequested(); + if (LocationType == LocationType.FileSystem && Parent != null) + { + themeSongsChanged = await RefreshThemeSongs(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + + themeVideosChanged = await RefreshThemeVideos(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + + localTrailersChanged = await RefreshLocalTrailers(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + } cancellationToken.ThrowIfCancellationRequested(); @@ -1096,8 +1088,7 @@ namespace MediaBrowser.Controller.Entities parent = parent.Parent; } - //not found - load from repo - return LibraryManager.RetrieveItem(id); + return null; } /// <summary> @@ -1315,7 +1306,7 @@ namespace MediaBrowser.Controller.Entities var key = GetUserDataKey(); - var data = await userManager.GetUserData(user.Id, key).ConfigureAwait(false); + var data = userManager.GetUserData(user.Id, key); if (wasPlayed) { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index ce36366b43..de965221b8 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Reflection; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using System; @@ -21,6 +22,15 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class Folder : BaseItem { + private static TypeMapper _typeMapper = new TypeMapper(); + + public Folder() + { + ChildDefinitions = new ConcurrentDictionary<Guid, string>(); + } + + public ConcurrentDictionary<Guid, string> ChildDefinitions { get; set; } + /// <summary> /// Gets a value indicating whether this instance is folder. /// </summary> @@ -108,16 +118,14 @@ namespace MediaBrowser.Controller.Entities item.DateModified = DateTime.Now; } - if (!_children.TryAdd(item.Id, item)) + if (!_children.TryAdd(item.Id, item) || !ChildDefinitions.TryAdd(item.Id, item.GetType().FullName)) { throw new InvalidOperationException("Unable to add " + item.Name); } - var newChildren = Children.ToList(); - await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); - await LibraryManager.SaveChildren(Id, newChildren, cancellationToken).ConfigureAwait(false); + await LibraryManager.UpdateItem(this, cancellationToken).ConfigureAwait(false); } /// <summary> @@ -145,19 +153,18 @@ namespace MediaBrowser.Controller.Entities public Task RemoveChild(BaseItem item, CancellationToken cancellationToken) { BaseItem removed; + string removedType; - if (!_children.TryRemove(item.Id, out removed)) + if (!_children.TryRemove(item.Id, out removed) || !ChildDefinitions.TryRemove(item.Id, out removedType)) { throw new InvalidOperationException("Unable to remove " + item.Name); } item.Parent = null; - var newChildren = Children.ToList(); - LibraryManager.ReportItemRemoved(item); - return LibraryManager.SaveChildren(Id, newChildren, cancellationToken); + return LibraryManager.UpdateItem(this, cancellationToken); } #region Indexing @@ -652,7 +659,7 @@ namespace MediaBrowser.Controller.Entities var options = new ParallelOptions { - MaxDegreeOfParallelism = 50 + MaxDegreeOfParallelism = 20 }; Parallel.ForEach(nonCachedChildren, options, child => @@ -702,6 +709,9 @@ namespace MediaBrowser.Controller.Entities } else { + string removedType; + ChildDefinitions.TryRemove(item.Id, out removedType); + LibraryManager.ReportItemRemoved(item); } } @@ -716,11 +726,13 @@ namespace MediaBrowser.Controller.Entities } else { + ChildDefinitions.TryAdd(item.Id, item.GetType().FullName); + Logger.Debug("** " + item.Name + " Added to library."); } } - await LibraryManager.SaveChildren(Id, newChildren, CancellationToken.None).ConfigureAwait(false); + await LibraryManager.UpdateItem(this, CancellationToken.None).ConfigureAwait(false); //force the indexes to rebuild next time IndexCache.Clear(); @@ -848,9 +860,38 @@ namespace MediaBrowser.Controller.Entities /// Get our children from the repo - stubbed for now /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> - protected virtual IEnumerable<BaseItem> GetCachedChildren() + protected IEnumerable<BaseItem> GetCachedChildren() + { + var items = ChildDefinitions.ToList().Select(RetrieveChild).Where(i => i != null).ToList(); + + foreach (var item in items) + { + item.Parent = this; + } + + return items; + } + + /// <summary> + /// Retrieves the child. + /// </summary> + /// <param name="child">The child.</param> + /// <returns>BaseItem.</returns> + private BaseItem RetrieveChild(KeyValuePair<Guid,string> child) { - return LibraryManager.RetrieveChildren(this).Select(i => i is IByReferenceItem ? LibraryManager.GetOrAddByReferenceItem(i) : i); + var type = child.Value; + + var itemType = _typeMapper.GetType(type); + + if (itemType == null) + { + Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); + return null; + } + + var item = LibraryManager.RetrieveItem(child.Key, itemType); + + return item is IByReferenceItem ? LibraryManager.GetOrAddByReferenceItem(item) : item; } /// <summary> diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 307fe19544..6e649fd656 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -68,7 +68,14 @@ namespace MediaBrowser.Controller.Entities.Movies // Kick off a task to refresh the main item var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); - var specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var specialFeaturesChanged = false; + + // Must have a parent to have special features + // In other words, it must be part of the Parent/Child tree + if (LocationType == LocationType.FileSystem && Parent != null) + { + specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + } return specialFeaturesChanged || result; } @@ -95,11 +102,6 @@ namespace MediaBrowser.Controller.Entities.Movies /// <returns>IEnumerable{Video}.</returns> private IEnumerable<Video> LoadSpecialFeatures() { - if (LocationType != LocationType.FileSystem) - { - return new List<Video>(); - } - FileSystemInfo folder; try @@ -133,7 +135,7 @@ namespace MediaBrowser.Controller.Entities.Movies return LibraryManager.ResolvePaths<Video>(files, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(video.Id) as Video; + var dbItem = LibraryManager.RetrieveItem(video.Id, typeof(Video)) as Video; if (dbItem != null) { diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index ad4cb2d09d..ee717a1918 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -139,7 +139,10 @@ namespace MediaBrowser.Controller.Entities var additionalPartsChanged = false; - if (IsMultiPart && LocationType == LocationType.FileSystem) + // Must have a parent to have additional parts + // In other words, it must be part of the Parent/Child tree + // The additional parts won't have additional parts themselves + if (IsMultiPart && LocationType == LocationType.FileSystem && Parent != null) { try { @@ -164,11 +167,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>Task{System.Boolean}.</returns> private async Task<bool> RefreshAdditionalParts(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true) { - if (!IsMultiPart || LocationType != LocationType.FileSystem) - { - return false; - } - var newItems = LoadAdditionalParts().ToList(); var newItemIds = newItems.Select(i => i.Id).ToList(); @@ -214,7 +212,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths<Video>(files, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(video.Id) as Video; + var dbItem = LibraryManager.RetrieveItem(video.Id, typeof(Video)) as Video; if (dbItem != null) { diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 7e84350b37..6d6fab3bea 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -216,24 +216,9 @@ namespace MediaBrowser.Controller.Library /// Retrieves the item. /// </summary> /// <param name="id">The id.</param> - /// <returns>Task{BaseItem}.</returns> - BaseItem RetrieveItem(Guid id); - - /// <summary> - /// Saves the children. - /// </summary> - /// <param name="id">The id.</param> - /// <param name="children">The children.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken); - - /// <summary> - /// Retrieves the children. - /// </summary> - /// <param name="parent">The parent.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - IEnumerable<BaseItem> RetrieveChildren(Folder parent); + /// <param name="type">The type.</param> + /// <returns>BaseItem.</returns> + BaseItem RetrieveItem(Guid id, Type type); /// <summary> /// Validates the artists. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index ba9e9f5bde..8765998f38 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -80,6 +80,7 @@ <Compile Include="Library\ILibraryPrescanTask.cs" /> <Compile Include="Library\IMetadataSaver.cs" /> <Compile Include="Localization\ILocalizationManager.cs" /> + <Compile Include="Reflection\TypeMapper.cs" /> <Compile Include="Session\ISessionManager.cs" /> <Compile Include="Drawing\ImageExtensions.cs" /> <Compile Include="Drawing\ImageHeader.cs" /> diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index bf3bc626af..d854d0e207 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -1,9 +1,10 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Persistence { @@ -21,36 +22,6 @@ namespace MediaBrowser.Controller.Persistence Task SaveItem(BaseItem item, CancellationToken cancellationToken); /// <summary> - /// Gets an item - /// </summary> - /// <param name="id">The id.</param> - /// <returns>BaseItem.</returns> - BaseItem GetItem(Guid id); - - /// <summary> - /// Gets children of a given Folder - /// </summary> - /// <param name="parent">The parent.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - IEnumerable<BaseItem> RetrieveChildren(Folder parent); - - /// <summary> - /// Retrieves the items. - /// </summary> - /// <param name="ids">The ids.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - IEnumerable<BaseItem> GetItems(IEnumerable<Guid> ids); - - /// <summary> - /// Saves children of a given Folder - /// </summary> - /// <param name="parentId">The parent id.</param> - /// <param name="children">The children.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SaveChildren(Guid parentId, IEnumerable<BaseItem> children, CancellationToken cancellationToken); - - /// <summary> /// Gets the critic reviews. /// </summary> /// <param name="itemId">The item id.</param> @@ -72,5 +43,46 @@ namespace MediaBrowser.Controller.Persistence /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> Task SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken); + + /// <summary> + /// Retrieves the item. + /// </summary> + /// <param name="id">The id.</param> + /// <param name="type">The type.</param> + /// <returns>BaseItem.</returns> + BaseItem RetrieveItem(Guid id, Type type); + } + + /// <summary> + /// Class ItemRepositoryExtensions + /// </summary> + public static class ItemRepositoryExtensions + { + /// <summary> + /// Retrieves the item. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="repository">The repository.</param> + /// <param name="id">The id.</param> + /// <returns>``0.</returns> + public static T RetrieveItem<T>(this IItemRepository repository, Guid id) + where T : BaseItem, new() + { + return repository.RetrieveItem(id, typeof(T)) as T; + } + + /// <summary> + /// Retrieves the item. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="repository">The repository.</param> + /// <param name="idList">The id list.</param> + /// <returns>IEnumerable{``0}.</returns> + public static IEnumerable<T> RetrieveItems<T>(this IItemRepository repository, IEnumerable<Guid> idList) + where T : BaseItem, new() + { + return idList.Select(repository.RetrieveItem<T>).Where(i => i != null); + } } } + diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 1b4efc58b1..ad111f4ed4 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -27,6 +27,6 @@ namespace MediaBrowser.Controller.Persistence /// <param name="userId">The user id.</param> /// <param name="key">The key.</param> /// <returns>Task{UserItemData}.</returns> - Task<UserItemData> GetUserData(Guid userId, string key); + UserItemData GetUserData(Guid userId, string key); } } diff --git a/MediaBrowser.Controller/Reflection/TypeMapper.cs b/MediaBrowser.Controller/Reflection/TypeMapper.cs new file mode 100644 index 0000000000..d968a3b420 --- /dev/null +++ b/MediaBrowser.Controller/Reflection/TypeMapper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; + +namespace MediaBrowser.Controller.Reflection +{ + /// <summary> + /// Class TypeMapper + /// </summary> + public class TypeMapper + { + /// <summary> + /// This holds all the types in the running assemblies so that we can de-serialize properly when we don't have strong types + /// </summary> + private readonly ConcurrentDictionary<string, Type> _typeMap = new ConcurrentDictionary<string, Type>(); + + /// <summary> + /// Gets the type. + /// </summary> + /// <param name="typeName">Name of the type.</param> + /// <returns>Type.</returns> + /// <exception cref="System.ArgumentNullException"></exception> + public Type GetType(string typeName) + { + if (string.IsNullOrEmpty(typeName)) + { + throw new ArgumentNullException(); + } + + return _typeMap.GetOrAdd(typeName, LookupType); + } + + /// <summary> + /// Lookups the type. + /// </summary> + /// <param name="typeName">Name of the type.</param> + /// <returns>Type.</returns> + private Type LookupType(string typeName) + { + return AppDomain + .CurrentDomain + .GetAssemblies() + .Select(a => a.GetType(typeName, false)) + .FirstOrDefault(t => t != null); + } + } +} |
