001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.asset.service.impl;
016    
017    import com.liferay.portal.kernel.cache.ThreadLocalCachable;
018    import com.liferay.portal.kernel.dao.orm.QueryUtil;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.search.BaseModelSearchResult;
021    import com.liferay.portal.kernel.search.Document;
022    import com.liferay.portal.kernel.search.Field;
023    import com.liferay.portal.kernel.search.Hits;
024    import com.liferay.portal.kernel.search.Indexable;
025    import com.liferay.portal.kernel.search.IndexableType;
026    import com.liferay.portal.kernel.search.Indexer;
027    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
028    import com.liferay.portal.kernel.search.QueryConfig;
029    import com.liferay.portal.kernel.search.SearchContext;
030    import com.liferay.portal.kernel.search.SearchException;
031    import com.liferay.portal.kernel.systemevent.SystemEvent;
032    import com.liferay.portal.kernel.transaction.TransactionCommitCallbackRegistryUtil;
033    import com.liferay.portal.kernel.util.CharPool;
034    import com.liferay.portal.kernel.util.GetterUtil;
035    import com.liferay.portal.kernel.util.ListUtil;
036    import com.liferay.portal.kernel.util.LocaleUtil;
037    import com.liferay.portal.kernel.util.OrderByComparator;
038    import com.liferay.portal.kernel.util.StringBundler;
039    import com.liferay.portal.kernel.util.StringPool;
040    import com.liferay.portal.kernel.util.StringUtil;
041    import com.liferay.portal.kernel.util.Validator;
042    import com.liferay.portal.model.ModelHintsUtil;
043    import com.liferay.portal.model.ResourceConstants;
044    import com.liferay.portal.model.SystemEventConstants;
045    import com.liferay.portal.model.User;
046    import com.liferay.portal.service.ServiceContext;
047    import com.liferay.portlet.asset.AssetCategoryNameException;
048    import com.liferay.portlet.asset.DuplicateCategoryException;
049    import com.liferay.portlet.asset.model.AssetCategory;
050    import com.liferay.portlet.asset.model.AssetCategoryConstants;
051    import com.liferay.portlet.asset.model.AssetCategoryProperty;
052    import com.liferay.portlet.asset.model.AssetEntry;
053    import com.liferay.portlet.asset.service.base.AssetCategoryLocalServiceBaseImpl;
054    import com.liferay.portlet.asset.util.comparator.AssetCategoryLeftCategoryIdComparator;
055    
056    import java.io.Serializable;
057    
058    import java.util.ArrayList;
059    import java.util.Collections;
060    import java.util.HashMap;
061    import java.util.Iterator;
062    import java.util.List;
063    import java.util.Locale;
064    import java.util.Map;
065    import java.util.concurrent.Callable;
066    
067    /**
068     * Provides the local service for accessing, adding, deleting, merging, moving,
069     * and updating asset categories.
070     *
071     * @author Brian Wing Shun Chan
072     * @author Alvaro del Castillo
073     * @author Jorge Ferrer
074     * @author Bruno Farache
075     */
076    public class AssetCategoryLocalServiceImpl
077            extends AssetCategoryLocalServiceBaseImpl {
078    
079            @Indexable(type = IndexableType.REINDEX)
080            @Override
081            public AssetCategory addCategory(
082                            long userId, long groupId, long parentCategoryId,
083                            Map<Locale, String> titleMap, Map<Locale, String> descriptionMap,
084                            long vocabularyId, String[] categoryProperties,
085                            ServiceContext serviceContext)
086                    throws PortalException {
087    
088                    // Category
089    
090                    User user = userPersistence.findByPrimaryKey(userId);
091    
092                    String name = titleMap.get(LocaleUtil.getSiteDefault());
093    
094                    name = ModelHintsUtil.trimString(
095                            AssetCategory.class.getName(), "name", name);
096    
097                    if (categoryProperties == null) {
098                            categoryProperties = new String[0];
099                    }
100    
101                    validate(0, parentCategoryId, name, vocabularyId);
102    
103                    if (parentCategoryId > 0) {
104                            assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
105                    }
106    
107                    assetVocabularyPersistence.findByPrimaryKey(vocabularyId);
108    
109                    long categoryId = counterLocalService.increment();
110    
111                    AssetCategory category = assetCategoryPersistence.create(categoryId);
112    
113                    category.setUuid(serviceContext.getUuid());
114                    category.setGroupId(groupId);
115                    category.setCompanyId(user.getCompanyId());
116                    category.setUserId(user.getUserId());
117                    category.setUserName(user.getFullName());
118                    category.setParentCategoryId(parentCategoryId);
119                    category.setName(name);
120                    category.setTitleMap(titleMap);
121                    category.setDescriptionMap(descriptionMap);
122                    category.setVocabularyId(vocabularyId);
123    
124                    assetCategoryPersistence.update(category);
125    
126                    // Resources
127    
128                    if (serviceContext.isAddGroupPermissions() ||
129                            serviceContext.isAddGuestPermissions()) {
130    
131                            addCategoryResources(
132                                    category, serviceContext.isAddGroupPermissions(),
133                                    serviceContext.isAddGuestPermissions());
134                    }
135                    else {
136                            addCategoryResources(
137                                    category, serviceContext.getGroupPermissions(),
138                                    serviceContext.getGuestPermissions());
139                    }
140    
141                    // Properties
142    
143                    for (int i = 0; i < categoryProperties.length; i++) {
144                            String[] categoryProperty = StringUtil.split(
145                                    categoryProperties[i],
146                                    AssetCategoryConstants.PROPERTY_KEY_VALUE_SEPARATOR);
147    
148                            if (categoryProperty.length <= 1) {
149                                    categoryProperty = StringUtil.split(
150                                            categoryProperties[i], CharPool.COLON);
151                            }
152    
153                            String key = StringPool.BLANK;
154                            String value = StringPool.BLANK;
155    
156                            if (categoryProperty.length > 1) {
157                                    key = GetterUtil.getString(categoryProperty[0]);
158                                    value = GetterUtil.getString(categoryProperty[1]);
159                            }
160    
161                            if (Validator.isNotNull(key)) {
162                                    assetCategoryPropertyLocalService.addCategoryProperty(
163                                            userId, categoryId, key, value);
164                            }
165                    }
166    
167                    return category;
168            }
169    
170            @Override
171            public AssetCategory addCategory(
172                            long userId, long groupId, String title, long vocabularyId,
173                            ServiceContext serviceContext)
174                    throws PortalException {
175    
176                    Map<Locale, String> titleMap = new HashMap<>();
177    
178                    Locale locale = LocaleUtil.getSiteDefault();
179    
180                    titleMap.put(locale, title);
181    
182                    Map<Locale, String> descriptionMap = new HashMap<>();
183    
184                    descriptionMap.put(locale, StringPool.BLANK);
185    
186                    return assetCategoryLocalService.addCategory(
187                            userId, groupId, AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID,
188                            titleMap, descriptionMap, vocabularyId, null, serviceContext);
189            }
190    
191            @Override
192            public void addCategoryResources(
193                            AssetCategory category, boolean addGroupPermissions,
194                            boolean addGuestPermissions)
195                    throws PortalException {
196    
197                    resourceLocalService.addResources(
198                            category.getCompanyId(), category.getGroupId(),
199                            category.getUserId(), AssetCategory.class.getName(),
200                            category.getCategoryId(), false, addGroupPermissions,
201                            addGuestPermissions);
202            }
203    
204            @Override
205            public void addCategoryResources(
206                            AssetCategory category, String[] groupPermissions,
207                            String[] guestPermissions)
208                    throws PortalException {
209    
210                    resourceLocalService.addModelResources(
211                            category.getCompanyId(), category.getGroupId(),
212                            category.getUserId(), AssetCategory.class.getName(),
213                            category.getCategoryId(), groupPermissions, guestPermissions);
214            }
215    
216            @Override
217            public void deleteCategories(List<AssetCategory> categories)
218                    throws PortalException {
219    
220                    List<Long> rebuildTreeGroupIds = new ArrayList<>();
221    
222                    for (AssetCategory category : categories) {
223                            if (!rebuildTreeGroupIds.contains(category.getGroupId()) &&
224                                    (getChildCategoriesCount(category.getCategoryId()) > 0)) {
225    
226                                    final long groupId = category.getGroupId();
227    
228                                    TransactionCommitCallbackRegistryUtil.registerCallback(
229                                            new Callable<Void>() {
230    
231                                                    @Override
232                                                    public Void call() throws Exception {
233                                                            assetCategoryLocalService.rebuildTree(
234                                                                    groupId, true);
235    
236                                                            return null;
237                                                    }
238    
239                                            });
240    
241                                    rebuildTreeGroupIds.add(groupId);
242                            }
243    
244                            assetCategoryLocalService.deleteCategory(category, true);
245                    }
246            }
247    
248            @Override
249            public void deleteCategories(long[] categoryIds) throws PortalException {
250                    List<AssetCategory> categories = new ArrayList<>();
251    
252                    for (long categoryId : categoryIds) {
253                            AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
254                                    categoryId);
255    
256                            categories.add(category);
257                    }
258    
259                    deleteCategories(categories);
260            }
261    
262            @Override
263            public AssetCategory deleteCategory(AssetCategory category)
264                    throws PortalException {
265    
266                    return assetCategoryLocalService.deleteCategory(category, false);
267            }
268    
269            @Indexable(type = IndexableType.DELETE)
270            @Override
271            @SystemEvent(type = SystemEventConstants.TYPE_DELETE)
272            public AssetCategory deleteCategory(
273                            AssetCategory category, boolean skipRebuildTree)
274                    throws PortalException {
275    
276                    // Categories
277    
278                    List<AssetCategory> categories =
279                            assetCategoryPersistence.findByParentCategoryId(
280                                    category.getCategoryId());
281    
282                    for (AssetCategory curCategory : categories) {
283                            deleteCategory(curCategory, true);
284                    }
285    
286                    if (!categories.isEmpty() && !skipRebuildTree) {
287                            final long groupId = category.getGroupId();
288    
289                            TransactionCommitCallbackRegistryUtil.registerCallback(
290                                    new Callable<Void>() {
291    
292                                            @Override
293                                            public Void call() throws Exception {
294                                                    assetCategoryLocalService.rebuildTree(groupId, true);
295    
296                                                    return null;
297                                            }
298    
299                                    });
300                    }
301    
302                    // Entries
303    
304                    List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(
305                            category.getCategoryId());
306    
307                    // Category
308    
309                    assetCategoryPersistence.remove(category);
310    
311                    // Resources
312    
313                    resourceLocalService.deleteResource(
314                            category.getCompanyId(), AssetCategory.class.getName(),
315                            ResourceConstants.SCOPE_INDIVIDUAL, category.getCategoryId());
316    
317                    // Properties
318    
319                    assetCategoryPropertyLocalService.deleteCategoryProperties(
320                            category.getCategoryId());
321    
322                    // Indexer
323    
324                    assetEntryLocalService.reindex(entries);
325    
326                    return category;
327            }
328    
329            @Override
330            public AssetCategory deleteCategory(long categoryId)
331                    throws PortalException {
332    
333                    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
334                            categoryId);
335    
336                    return assetCategoryLocalService.deleteCategory(category);
337            }
338    
339            @Override
340            public void deleteVocabularyCategories(long vocabularyId)
341                    throws PortalException {
342    
343                    List<AssetCategory> categories = assetCategoryPersistence.findByP_V(
344                            AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID, vocabularyId,
345                            QueryUtil.ALL_POS, QueryUtil.ALL_POS,
346                            new AssetCategoryLeftCategoryIdComparator(false));
347    
348                    assetCategoryLocalService.deleteCategories(categories);
349            }
350    
351            @Override
352            public AssetCategory fetchCategory(long categoryId) {
353                    return assetCategoryPersistence.fetchByPrimaryKey(categoryId);
354            }
355    
356            @Override
357            public List<AssetCategory> getCategories() {
358                    return assetCategoryPersistence.findAll();
359            }
360    
361            @Override
362            public List<AssetCategory> getCategories(Hits hits) throws PortalException {
363                    List<Document> documents = hits.toList();
364    
365                    List<AssetCategory> categories = new ArrayList<>(documents.size());
366    
367                    for (Document document : documents) {
368                            long categoryId = GetterUtil.getLong(
369                                    document.get(Field.ASSET_CATEGORY_ID));
370    
371                            AssetCategory category = fetchCategory(categoryId);
372    
373                            if (category == null) {
374                                    categories = null;
375    
376                                    Indexer indexer = IndexerRegistryUtil.getIndexer(
377                                            AssetCategory.class);
378    
379                                    long companyId = GetterUtil.getLong(
380                                            document.get(Field.COMPANY_ID));
381    
382                                    indexer.delete(companyId, document.getUID());
383                            }
384                            else if (categories != null) {
385                                    categories.add(category);
386                            }
387                    }
388    
389                    return categories;
390            }
391    
392            @Override
393            @ThreadLocalCachable
394            public List<AssetCategory> getCategories(long classNameId, long classPK) {
395                    AssetEntry entry = assetEntryPersistence.fetchByC_C(
396                            classNameId, classPK);
397    
398                    if (entry == null) {
399                            return Collections.emptyList();
400                    }
401    
402                    return assetEntryPersistence.getAssetCategories(entry.getEntryId());
403            }
404    
405            @Override
406            public List<AssetCategory> getCategories(String className, long classPK) {
407                    long classNameId = classNameLocalService.getClassNameId(className);
408    
409                    return getCategories(classNameId, classPK);
410            }
411    
412            @Override
413            public AssetCategory getCategory(long categoryId) throws PortalException {
414                    return assetCategoryPersistence.findByPrimaryKey(categoryId);
415            }
416    
417            @Override
418            public AssetCategory getCategory(String uuid, long groupId)
419                    throws PortalException {
420    
421                    return assetCategoryPersistence.findByUUID_G(uuid, groupId);
422            }
423    
424            @Override
425            public long[] getCategoryIds(String className, long classPK) {
426                    return getCategoryIds(getCategories(className, classPK));
427            }
428    
429            @Override
430            public String[] getCategoryNames() {
431                    return getCategoryNames(getCategories());
432            }
433    
434            @Override
435            public String[] getCategoryNames(long classNameId, long classPK) {
436                    return getCategoryNames(getCategories(classNameId, classPK));
437            }
438    
439            @Override
440            public String[] getCategoryNames(String className, long classPK) {
441                    return getCategoryNames(getCategories(className, classPK));
442            }
443    
444            @Override
445            public List<AssetCategory> getChildCategories(long parentCategoryId) {
446                    return assetCategoryPersistence.findByParentCategoryId(
447                            parentCategoryId);
448            }
449    
450            @Override
451            public List<AssetCategory> getChildCategories(
452                    long parentCategoryId, int start, int end,
453                    OrderByComparator<AssetCategory> obc) {
454    
455                    return assetCategoryPersistence.findByParentCategoryId(
456                            parentCategoryId, start, end, obc);
457            }
458    
459            @Override
460            public int getChildCategoriesCount(long parentCategoryId) {
461                    return assetCategoryPersistence.countByParentCategoryId(
462                            parentCategoryId);
463            }
464    
465            @Override
466            public List<AssetCategory> getEntryCategories(long entryId) {
467                    return assetEntryPersistence.getAssetCategories(entryId);
468            }
469    
470            @Override
471            public List<Long> getSubcategoryIds(long parentCategoryId) {
472                    AssetCategory parentAssetCategory =
473                            assetCategoryPersistence.fetchByPrimaryKey(parentCategoryId);
474    
475                    if (parentAssetCategory == null) {
476                            return Collections.emptyList();
477                    }
478    
479                    return ListUtil.toList(
480                            assetCategoryPersistence.getDescendants(parentAssetCategory),
481                            AssetCategory.CATEGORY_ID_ACCESSOR);
482            }
483    
484            @Override
485            public List<AssetCategory> getVocabularyCategories(
486                    long vocabularyId, int start, int end,
487                    OrderByComparator<AssetCategory> obc) {
488    
489                    return assetCategoryPersistence.findByVocabularyId(
490                            vocabularyId, start, end, obc);
491            }
492    
493            @Override
494            public List<AssetCategory> getVocabularyCategories(
495                    long parentCategoryId, long vocabularyId, int start, int end,
496                    OrderByComparator<AssetCategory> obc) {
497    
498                    return assetCategoryPersistence.findByP_V(
499                            parentCategoryId, vocabularyId, start, end, obc);
500            }
501    
502            @Override
503            public int getVocabularyCategoriesCount(long vocabularyId) {
504                    return assetCategoryPersistence.countByVocabularyId(vocabularyId);
505            }
506    
507            @Override
508            public List<AssetCategory> getVocabularyRootCategories(
509                    long vocabularyId, int start, int end,
510                    OrderByComparator<AssetCategory> obc) {
511    
512                    return getVocabularyCategories(
513                            AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID, vocabularyId,
514                            start, end, obc);
515            }
516    
517            @Override
518            public int getVocabularyRootCategoriesCount(long vocabularyId) {
519                    return assetCategoryPersistence.countByP_V(
520                            AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID, vocabularyId);
521            }
522    
523            @Indexable(type = IndexableType.REINDEX)
524            @Override
525            public AssetCategory mergeCategories(long fromCategoryId, long toCategoryId)
526                    throws PortalException {
527    
528                    List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(
529                            fromCategoryId);
530    
531                    assetCategoryPersistence.addAssetEntries(toCategoryId, entries);
532    
533                    List<AssetCategoryProperty> categoryProperties =
534                            assetCategoryPropertyPersistence.findByCategoryId(fromCategoryId);
535    
536                    for (AssetCategoryProperty fromCategoryProperty : categoryProperties) {
537                            AssetCategoryProperty toCategoryProperty =
538                                    assetCategoryPropertyPersistence.fetchByCA_K(
539                                            toCategoryId, fromCategoryProperty.getKey());
540    
541                            if (toCategoryProperty == null) {
542                                    fromCategoryProperty.setCategoryId(toCategoryId);
543    
544                                    assetCategoryPropertyPersistence.update(fromCategoryProperty);
545                            }
546                    }
547    
548                    assetCategoryLocalService.deleteCategory(fromCategoryId);
549    
550                    return getCategory(toCategoryId);
551            }
552    
553            @Indexable(type = IndexableType.REINDEX)
554            @Override
555            public AssetCategory moveCategory(
556                            long categoryId, long parentCategoryId, long vocabularyId,
557                            ServiceContext serviceContext)
558                    throws PortalException {
559    
560                    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
561                            categoryId);
562    
563                    validate(
564                            categoryId, parentCategoryId, category.getName(), vocabularyId);
565    
566                    if (parentCategoryId > 0) {
567                            assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
568                    }
569    
570                    if (vocabularyId != category.getVocabularyId()) {
571                            assetVocabularyPersistence.findByPrimaryKey(vocabularyId);
572    
573                            category.setVocabularyId(vocabularyId);
574    
575                            updateChildrenVocabularyId(category, vocabularyId);
576                    }
577    
578                    category.setParentCategoryId(parentCategoryId);
579    
580                    assetCategoryPersistence.update(category);
581    
582                    return category;
583            }
584    
585            @Override
586            public void rebuildTree(long groupId, boolean force) {
587                    assetCategoryPersistence.rebuildTree(groupId, force);
588            }
589    
590            @Override
591            public List<AssetCategory> search(
592                    long groupId, String name, String[] categoryProperties, int start,
593                    int end) {
594    
595                    return assetCategoryFinder.findByG_N_P(
596                            groupId, name, categoryProperties, start, end);
597            }
598    
599            @Override
600            public BaseModelSearchResult<AssetCategory> searchCategories(
601                            long companyId, long groupIds, String title, long vocabularyId,
602                            int start, int end)
603                    throws PortalException {
604    
605                    return searchCategories(
606                            companyId, new long[] {groupIds}, title, new long[] {vocabularyId},
607                            start, end);
608            }
609    
610            @Override
611            public BaseModelSearchResult<AssetCategory> searchCategories(
612                            long companyId, long[] groupIds, String title, long[] vocabularyIds,
613                            int start, int end)
614                    throws PortalException {
615    
616                    SearchContext searchContext = buildSearchContext(
617                            companyId, groupIds, title, new long[0], vocabularyIds, start, end);
618    
619                    return searchCategories(searchContext);
620            }
621    
622            @Override
623            public BaseModelSearchResult<AssetCategory> searchCategories(
624                            long companyId, long[] groupIds, String title,
625                            long[] parentCategoryIds, long[] vocabularyIds, int start, int end)
626                    throws PortalException {
627    
628                    SearchContext searchContext = buildSearchContext(
629                            companyId, groupIds, title, parentCategoryIds, vocabularyIds, start,
630                            end);
631    
632                    return searchCategories(searchContext);
633            }
634    
635            @Indexable(type = IndexableType.REINDEX)
636            @Override
637            public AssetCategory updateCategory(
638                            long userId, long categoryId, long parentCategoryId,
639                            Map<Locale, String> titleMap, Map<Locale, String> descriptionMap,
640                            long vocabularyId, String[] categoryProperties,
641                            ServiceContext serviceContext)
642                    throws PortalException {
643    
644                    // Category
645    
646                    String name = titleMap.get(LocaleUtil.getSiteDefault());
647    
648                    name = ModelHintsUtil.trimString(
649                            AssetCategory.class.getName(), "name", name);
650    
651                    if (categoryProperties == null) {
652                            categoryProperties = new String[0];
653                    }
654    
655                    validate(categoryId, parentCategoryId, name, vocabularyId);
656    
657                    if (parentCategoryId > 0) {
658                            assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
659                    }
660    
661                    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
662                            categoryId);
663    
664                    String oldName = category.getName();
665    
666                    if (vocabularyId != category.getVocabularyId()) {
667                            assetVocabularyPersistence.findByPrimaryKey(vocabularyId);
668    
669                            parentCategoryId =
670                                    AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
671    
672                            category.setVocabularyId(vocabularyId);
673    
674                            updateChildrenVocabularyId(category, vocabularyId);
675                    }
676    
677                    category.setParentCategoryId(parentCategoryId);
678                    category.setName(name);
679                    category.setTitleMap(titleMap);
680                    category.setDescriptionMap(descriptionMap);
681    
682                    assetCategoryPersistence.update(category);
683    
684                    // Properties
685    
686                    List<AssetCategoryProperty> oldCategoryProperties =
687                            assetCategoryPropertyPersistence.findByCategoryId(categoryId);
688    
689                    oldCategoryProperties = ListUtil.copy(oldCategoryProperties);
690    
691                    for (int i = 0; i < categoryProperties.length; i++) {
692                            String[] categoryProperty = StringUtil.split(
693                                    categoryProperties[i],
694                                    AssetCategoryConstants.PROPERTY_KEY_VALUE_SEPARATOR);
695    
696                            if (categoryProperty.length <= 1) {
697                                    categoryProperty = StringUtil.split(
698                                            categoryProperties[i], CharPool.COLON);
699                            }
700    
701                            String key = StringPool.BLANK;
702    
703                            if (categoryProperty.length > 0) {
704                                    key = GetterUtil.getString(categoryProperty[0]);
705                            }
706    
707                            String value = StringPool.BLANK;
708    
709                            if (categoryProperty.length > 1) {
710                                    value = GetterUtil.getString(categoryProperty[1]);
711                            }
712    
713                            if (Validator.isNotNull(key)) {
714                                    boolean addCategoryProperty = true;
715    
716                                    AssetCategoryProperty oldCategoryProperty = null;
717    
718                                    Iterator<AssetCategoryProperty> iterator =
719                                            oldCategoryProperties.iterator();
720    
721                                    while (iterator.hasNext()) {
722                                            oldCategoryProperty = iterator.next();
723    
724                                            if ((categoryId == oldCategoryProperty.getCategoryId()) &&
725                                                    key.equals(oldCategoryProperty.getKey())) {
726    
727                                                    addCategoryProperty = false;
728    
729                                                    if (!value.equals(oldCategoryProperty.getValue())) {
730                                                            assetCategoryPropertyLocalService.
731                                                                    updateCategoryProperty(
732                                                                            userId,
733                                                                            oldCategoryProperty.getCategoryPropertyId(),
734                                                                            key, value);
735                                                    }
736    
737                                                    iterator.remove();
738    
739                                                    break;
740                                            }
741                                    }
742    
743                                    if (addCategoryProperty) {
744                                            assetCategoryPropertyLocalService.addCategoryProperty(
745                                                    userId, categoryId, key, value);
746                                    }
747                            }
748                    }
749    
750                    for (AssetCategoryProperty categoryProperty : oldCategoryProperties) {
751                            assetCategoryPropertyLocalService.deleteAssetCategoryProperty(
752                                    categoryProperty);
753                    }
754    
755                    // Indexer
756    
757                    if (!oldName.equals(name)) {
758                            List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(
759                                    category.getCategoryId());
760    
761                            assetEntryLocalService.reindex(entries);
762                    }
763    
764                    return category;
765            }
766    
767            protected SearchContext buildSearchContext(
768                    long companyId, long[] groupIds, String title, long[] parentCategoryIds,
769                    long[] vocabularyIds, int start, int end) {
770    
771                    SearchContext searchContext = new SearchContext();
772    
773                    Map<String, Serializable> attributes = new HashMap<>();
774    
775                    attributes.put(Field.ASSET_PARENT_CATEGORY_IDS, parentCategoryIds);
776                    attributes.put(Field.ASSET_VOCABULARY_IDS, vocabularyIds);
777                    attributes.put(Field.TITLE, title);
778    
779                    searchContext.setAttributes(attributes);
780    
781                    searchContext.setCompanyId(companyId);
782                    searchContext.setEnd(end);
783                    searchContext.setGroupIds(groupIds);
784                    searchContext.setKeywords(title);
785                    searchContext.setStart(start);
786    
787                    QueryConfig queryConfig = searchContext.getQueryConfig();
788    
789                    queryConfig.setHighlightEnabled(false);
790                    queryConfig.setScoreEnabled(false);
791    
792                    return searchContext;
793            }
794    
795            protected long[] getCategoryIds(List<AssetCategory> categories) {
796                    return ListUtil.toLongArray(
797                            categories, AssetCategory.CATEGORY_ID_ACCESSOR);
798            }
799    
800            protected String[] getCategoryNames(List<AssetCategory> categories) {
801                    return ListUtil.toArray(categories, AssetCategory.NAME_ACCESSOR);
802            }
803    
804            protected BaseModelSearchResult<AssetCategory> searchCategories(
805                            SearchContext searchContext)
806                    throws PortalException {
807    
808                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
809                            AssetCategory.class);
810    
811                    for (int i = 0; i < 10; i++) {
812                            Hits hits = indexer.search(searchContext);
813    
814                            List<AssetCategory> categories = getCategories(hits);
815    
816                            if (categories != null) {
817                                    return new BaseModelSearchResult<>(
818                                            categories, hits.getLength());
819                            }
820                    }
821    
822                    throw new SearchException(
823                            "Unable to fix the search index after 10 attempts");
824            }
825    
826            protected void updateChildrenVocabularyId(
827                    AssetCategory category, long vocabularyId) {
828    
829                    List<AssetCategory> childrenCategories =
830                            assetCategoryPersistence.findByParentCategoryId(
831                                    category.getCategoryId());
832    
833                    if (!childrenCategories.isEmpty()) {
834                            for (AssetCategory childCategory : childrenCategories) {
835                                    childCategory.setVocabularyId(vocabularyId);
836    
837                                    assetCategoryPersistence.update(childCategory);
838    
839                                    updateChildrenVocabularyId(childCategory, vocabularyId);
840                            }
841                    }
842            }
843    
844            protected void validate(
845                            long categoryId, long parentCategoryId, String name,
846                            long vocabularyId)
847                    throws PortalException {
848    
849                    if (Validator.isNull(name)) {
850                            StringBundler sb = new StringBundler(5);
851    
852                            sb.append(
853                                    "Asset category name cannot be null for key {categoryId=");
854                            sb.append(categoryId);
855                            sb.append(", vocabularyId=");
856                            sb.append(vocabularyId);
857                            sb.append("}");
858    
859                            throw new AssetCategoryNameException(
860                                    "Category name cannot be null for category " + categoryId +
861                                            " and vocabulary " + vocabularyId);
862                    }
863    
864                    AssetCategory category = assetCategoryPersistence.fetchByP_N_V(
865                            parentCategoryId, name, vocabularyId);
866    
867                    if ((category != null) && (category.getCategoryId() != categoryId)) {
868                            StringBundler sb = new StringBundler(4);
869    
870                            sb.append("There is another category named ");
871                            sb.append(name);
872                            sb.append(" as a child of category ");
873                            sb.append(parentCategoryId);
874    
875                            throw new DuplicateCategoryException(sb.toString());
876                    }
877            }
878    
879    }