@Indexable(type = IndexableType.REINDEX)
  @Override
  public AssetCategory moveCategory(
      long categoryId, long parentCategoryId, long vocabularyId, ServiceContext serviceContext)
      throws PortalException {

    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(categoryId);

    validate(categoryId, parentCategoryId, category.getName(), vocabularyId);

    if (parentCategoryId > 0) {
      assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
    }

    if (vocabularyId != category.getVocabularyId()) {
      assetVocabularyPersistence.findByPrimaryKey(vocabularyId);

      category.setVocabularyId(vocabularyId);

      updateChildrenVocabularyId(category, vocabularyId);
    }

    category.setParentCategoryId(parentCategoryId);

    assetCategoryPersistence.update(category);

    return category;
  }
  protected void validate(long categoryId, long parentCategoryId, String name, long vocabularyId)
      throws PortalException {

    if (Validator.isNull(name)) {
      StringBundler sb = new StringBundler(5);

      sb.append("Asset category name cannot be null for key {categoryId=");
      sb.append(categoryId);
      sb.append(", vocabularyId=");
      sb.append(vocabularyId);
      sb.append("}");

      throw new AssetCategoryNameException(
          "Category name cannot be null for category "
              + categoryId
              + " and vocabulary "
              + vocabularyId);
    }

    AssetCategory category =
        assetCategoryPersistence.fetchByP_N_V(parentCategoryId, name, vocabularyId);

    if ((category != null) && (category.getCategoryId() != categoryId)) {
      StringBundler sb = new StringBundler(4);

      sb.append("There is another category named ");
      sb.append(name);
      sb.append(" as a child of category ");
      sb.append(parentCategoryId);

      throw new DuplicateCategoryException(sb.toString());
    }
  }
  @Override
  public void deleteCategories(List<AssetCategory> categories) throws PortalException {

    List<Long> rebuildTreeGroupIds = new ArrayList<>();

    for (AssetCategory category : categories) {
      if (!rebuildTreeGroupIds.contains(category.getGroupId())
          && (getChildCategoriesCount(category.getCategoryId()) > 0)) {

        final long groupId = category.getGroupId();

        TransactionCommitCallbackUtil.registerCallback(
            new Callable<Void>() {

              @Override
              public Void call() throws Exception {
                assetCategoryLocalService.rebuildTree(groupId, true);

                return null;
              }
            });

        rebuildTreeGroupIds.add(groupId);
      }

      assetCategoryLocalService.deleteCategory(category, true);
    }
  }
  @Override
  public void addCategoryResources(AssetCategory category, ModelPermissions modelPermissions)
      throws PortalException {

    resourceLocalService.addModelResources(
        category.getCompanyId(), category.getGroupId(),
        category.getUserId(), AssetCategory.class.getName(),
        category.getCategoryId(), modelPermissions);
  }
  protected void updateArticleType() throws Exception {
    if (!hasSelectedArticleTypes()) {
      return;
    }

    List<String> types = getArticleTypes();

    if (types.size() <= 0) {
      return;
    }

    Locale localeThreadLocalDefaultLocale = LocaleThreadLocal.getDefaultLocale();

    try {
      List<Company> companies = _companyLocalService.getCompanies();

      for (Company company : companies) {
        LocaleThreadLocal.setDefaultLocale(company.getLocale());

        Set<Locale> locales = LanguageUtil.getAvailableLocales(company.getGroupId());

        Locale defaultLocale =
            LocaleUtil.fromLanguageId(
                UpgradeProcessUtil.getDefaultLanguageId(company.getCompanyId()));

        Map<Locale, String> nameMap =
            LocalizationUtil.getLocalizationMap(locales, defaultLocale, "type");

        AssetVocabulary assetVocabulary =
            addAssetVocabulary(
                company.getGroupId(),
                company.getCompanyId(),
                "type",
                nameMap,
                new HashMap<Locale, String>());

        Map<String, Long> journalArticleTypesToAssetCategoryIds = new HashMap<>();

        for (String type : types) {
          AssetCategory assetCategory =
              addAssetCategory(
                  company.getGroupId(),
                  company.getCompanyId(),
                  type,
                  assetVocabulary.getVocabularyId());

          journalArticleTypesToAssetCategoryIds.put(type, assetCategory.getCategoryId());
        }

        updateArticles(company.getCompanyId(), journalArticleTypesToAssetCategoryIds);
      }
    } finally {
      LocaleThreadLocal.setDefaultLocale(localeThreadLocalDefaultLocale);
    }
  }
  protected void updateChildrenVocabularyId(AssetCategory category, long vocabularyId) {

    List<AssetCategory> childrenCategories =
        assetCategoryPersistence.findByParentCategoryId(category.getCategoryId());

    if (!childrenCategories.isEmpty()) {
      for (AssetCategory childCategory : childrenCategories) {
        childCategory.setVocabularyId(vocabularyId);

        assetCategoryPersistence.update(childCategory);

        updateChildrenVocabularyId(childCategory, vocabularyId);
      }
    }
  }
  @Override
  public void addCategoryResources(
      AssetCategory category, boolean addGroupPermissions, boolean addGuestPermissions)
      throws PortalException {

    resourceLocalService.addResources(
        category.getCompanyId(),
        category.getGroupId(),
        category.getUserId(),
        AssetCategory.class.getName(),
        category.getCategoryId(),
        false,
        addGroupPermissions,
        addGuestPermissions);
  }
  @Override
  protected StagedModel addStagedModel(
      Group group, Map<String, List<StagedModel>> dependentStagedModelsMap) throws Exception {

    List<StagedModel> vocabularyDependentStagedModels =
        dependentStagedModelsMap.get(AssetVocabulary.class.getSimpleName());

    AssetVocabulary vocabulary = (AssetVocabulary) vocabularyDependentStagedModels.get(0);

    List<StagedModel> categoryDependentStagedModels =
        dependentStagedModelsMap.get(AssetCategory.class.getSimpleName());

    AssetCategory category = (AssetCategory) categoryDependentStagedModels.get(0);

    return AssetTestUtil.addCategory(
        group.getGroupId(), vocabulary.getVocabularyId(), category.getCategoryId());
  }
  @Override
  protected void validateImportedStagedModel(
      StagedModel stagedModel, StagedModel importedStagedModel) throws Exception {

    super.validateImportedStagedModel(stagedModel, importedStagedModel);

    AssetCategory category = (AssetCategory) stagedModel;
    AssetCategory importedCategory = (AssetCategory) importedStagedModel;

    Assert.assertEquals(category.getName(), importedCategory.getName());
    Assert.assertEquals(category.getTitle(), importedCategory.getTitle());
    Assert.assertEquals(category.getDescription(), importedCategory.getDescription());
  }
  private long[] _getLeftAndRightCategoryIds(long[] categoryIds) {
    long[] leftRightIds = new long[categoryIds.length * 3];

    for (int i = 0; i < categoryIds.length; i++) {
      long categoryId = categoryIds[i];

      try {
        AssetCategory category = AssetCategoryLocalServiceUtil.getCategory(categoryId);

        leftRightIds[3 * i] = category.getGroupId();
        leftRightIds[3 * i + 1] = category.getLeftCategoryId();
        leftRightIds[3 * i + 2] = category.getRightCategoryId();
      } catch (Exception e) {
        if (_log.isWarnEnabled()) {
          _log.warn("Error retrieving category " + categoryId);
        }
      }
    }

    return leftRightIds;
  }
  @Override
  protected void validateImport(
      Map<String, List<StagedModel>> dependentStagedModelsMap, Group group) throws Exception {

    List<StagedModel> categoryDependentStagedModels =
        dependentStagedModelsMap.get(AssetCategory.class.getSimpleName());

    Assert.assertEquals(1, categoryDependentStagedModels.size());

    AssetCategory category = (AssetCategory) categoryDependentStagedModels.get(0);

    AssetCategoryLocalServiceUtil.getAssetCategoryByUuidAndGroupId(
        category.getUuid(), group.getGroupId());

    List<StagedModel> vocabularyDependentStagedModels =
        dependentStagedModelsMap.get(AssetVocabulary.class.getSimpleName());

    Assert.assertEquals(1, vocabularyDependentStagedModels.size());

    AssetVocabulary vocabulary = (AssetVocabulary) vocabularyDependentStagedModels.get(0);

    AssetVocabularyLocalServiceUtil.getAssetVocabularyByUuidAndGroupId(
        vocabulary.getUuid(), group.getGroupId());
  }
  @Indexable(type = IndexableType.DELETE)
  @Override
  @SystemEvent(type = SystemEventConstants.TYPE_DELETE)
  public AssetCategory deleteCategory(AssetCategory category, boolean skipRebuildTree)
      throws PortalException {

    // Categories

    List<AssetCategory> categories =
        assetCategoryPersistence.findByParentCategoryId(category.getCategoryId());

    for (AssetCategory curCategory : categories) {
      assetCategoryLocalService.deleteCategory(curCategory, true);
    }

    if (!categories.isEmpty() && !skipRebuildTree) {
      final long groupId = category.getGroupId();

      TransactionCommitCallbackUtil.registerCallback(
          new Callable<Void>() {

            @Override
            public Void call() throws Exception {
              assetCategoryLocalService.rebuildTree(groupId, true);

              return null;
            }
          });
    }

    // Entries

    List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(category.getCategoryId());

    // Category

    assetCategoryPersistence.remove(category);

    // Resources

    resourceLocalService.deleteResource(
        category.getCompanyId(),
        AssetCategory.class.getName(),
        ResourceConstants.SCOPE_INDIVIDUAL,
        category.getCategoryId());

    // Properties

    assetCategoryPropertyLocalService.deleteCategoryProperties(category.getCategoryId());

    // Indexer

    assetEntryLocalService.reindex(entries);

    return category;
  }
  @Indexable(type = IndexableType.REINDEX)
  @Override
  public AssetCategory addCategory(
      long userId,
      long groupId,
      long parentCategoryId,
      Map<Locale, String> titleMap,
      Map<Locale, String> descriptionMap,
      long vocabularyId,
      String[] categoryProperties,
      ServiceContext serviceContext)
      throws PortalException {

    // Category

    User user = userPersistence.findByPrimaryKey(userId);

    String name = titleMap.get(LocaleUtil.getSiteDefault());

    name = ModelHintsUtil.trimString(AssetCategory.class.getName(), "name", name);

    if (categoryProperties == null) {
      categoryProperties = new String[0];
    }

    validate(0, parentCategoryId, name, vocabularyId);

    if (parentCategoryId > 0) {
      assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
    }

    assetVocabularyPersistence.findByPrimaryKey(vocabularyId);

    long categoryId = counterLocalService.increment();

    AssetCategory category = assetCategoryPersistence.create(categoryId);

    category.setUuid(serviceContext.getUuid());
    category.setGroupId(groupId);
    category.setCompanyId(user.getCompanyId());
    category.setUserId(user.getUserId());
    category.setUserName(user.getFullName());
    category.setParentCategoryId(parentCategoryId);
    category.setName(name);
    category.setTitleMap(titleMap);
    category.setDescriptionMap(descriptionMap);
    category.setVocabularyId(vocabularyId);

    assetCategoryPersistence.update(category);

    // Resources

    if (serviceContext.isAddGroupPermissions() || serviceContext.isAddGuestPermissions()) {

      addCategoryResources(
          category, serviceContext.isAddGroupPermissions(), serviceContext.isAddGuestPermissions());
    } else {
      addCategoryResources(category, serviceContext.getModelPermissions());
    }

    // Properties

    for (int i = 0; i < categoryProperties.length; i++) {
      String[] categoryProperty =
          StringUtil.split(
              categoryProperties[i], AssetCategoryConstants.PROPERTY_KEY_VALUE_SEPARATOR);

      if (categoryProperty.length <= 1) {
        categoryProperty = StringUtil.split(categoryProperties[i], CharPool.COLON);
      }

      String key = StringPool.BLANK;
      String value = StringPool.BLANK;

      if (categoryProperty.length > 1) {
        key = GetterUtil.getString(categoryProperty[0]);
        value = GetterUtil.getString(categoryProperty[1]);
      }

      if (Validator.isNotNull(key)) {
        assetCategoryPropertyLocalService.addCategoryProperty(userId, categoryId, key, value);
      }
    }

    return category;
  }
  @Indexable(type = IndexableType.REINDEX)
  @Override
  public AssetCategory updateCategory(
      long userId,
      long categoryId,
      long parentCategoryId,
      Map<Locale, String> titleMap,
      Map<Locale, String> descriptionMap,
      long vocabularyId,
      String[] categoryProperties,
      ServiceContext serviceContext)
      throws PortalException {

    // Category

    String name = titleMap.get(LocaleUtil.getSiteDefault());

    name = ModelHintsUtil.trimString(AssetCategory.class.getName(), "name", name);

    if (categoryProperties == null) {
      categoryProperties = new String[0];
    }

    validate(categoryId, parentCategoryId, name, vocabularyId);

    if (parentCategoryId > 0) {
      assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
    }

    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(categoryId);

    String oldName = category.getName();

    if (vocabularyId != category.getVocabularyId()) {
      assetVocabularyPersistence.findByPrimaryKey(vocabularyId);

      parentCategoryId = AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;

      category.setVocabularyId(vocabularyId);

      updateChildrenVocabularyId(category, vocabularyId);
    }

    category.setParentCategoryId(parentCategoryId);
    category.setName(name);
    category.setTitleMap(titleMap);
    category.setDescriptionMap(descriptionMap);

    assetCategoryPersistence.update(category);

    // Properties

    List<AssetCategoryProperty> oldCategoryProperties =
        assetCategoryPropertyPersistence.findByCategoryId(categoryId);

    oldCategoryProperties = ListUtil.copy(oldCategoryProperties);

    for (int i = 0; i < categoryProperties.length; i++) {
      String[] categoryProperty =
          StringUtil.split(
              categoryProperties[i], AssetCategoryConstants.PROPERTY_KEY_VALUE_SEPARATOR);

      if (categoryProperty.length <= 1) {
        categoryProperty = StringUtil.split(categoryProperties[i], CharPool.COLON);
      }

      String key = StringPool.BLANK;

      if (categoryProperty.length > 0) {
        key = GetterUtil.getString(categoryProperty[0]);
      }

      String value = StringPool.BLANK;

      if (categoryProperty.length > 1) {
        value = GetterUtil.getString(categoryProperty[1]);
      }

      if (Validator.isNotNull(key)) {
        boolean addCategoryProperty = true;

        AssetCategoryProperty oldCategoryProperty = null;

        Iterator<AssetCategoryProperty> iterator = oldCategoryProperties.iterator();

        while (iterator.hasNext()) {
          oldCategoryProperty = iterator.next();

          if ((categoryId == oldCategoryProperty.getCategoryId())
              && key.equals(oldCategoryProperty.getKey())) {

            addCategoryProperty = false;

            if (!value.equals(oldCategoryProperty.getValue())) {
              assetCategoryPropertyLocalService.updateCategoryProperty(
                  userId, oldCategoryProperty.getCategoryPropertyId(), key, value);
            }

            iterator.remove();

            break;
          }
        }

        if (addCategoryProperty) {
          assetCategoryPropertyLocalService.addCategoryProperty(userId, categoryId, key, value);
        }
      }
    }

    for (AssetCategoryProperty categoryProperty : oldCategoryProperties) {
      assetCategoryPropertyLocalService.deleteAssetCategoryProperty(categoryProperty);
    }

    // Indexer

    if (!oldName.equals(name)) {
      List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(category.getCategoryId());

      assetEntryLocalService.reindex(entries);
    }

    return category;
  }