Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions celements-tag/src/main/java/com/celements/tag/CelTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static com.celements.spring.context.SpringContextProvider.*;
import static com.google.common.base.Preconditions.*;
import static java.util.stream.Collectors.*;
import static one.util.streamex.MoreCollectors.*;

import java.util.Comparator;
Expand Down Expand Up @@ -57,7 +56,7 @@ public static Builder builder() {
.getTagsByType()
.get(getType()).stream()
.filter(tag -> tag.getParent().filter(this::equals).isPresent())
.collect(toUnmodifiableList()));
.toList());

private CelTag(Builder builder) {
checkArgument(builder.hasAllDependencies());
Expand All @@ -70,7 +69,7 @@ private CelTag(Builder builder) {
this.dependencies = builder.dependencies.values().stream()
.filter(Objects::nonNull)
.filter(tag -> !tag.getName().equals(builder.parent))
.collect(toUnmodifiableList());
.toList();
this.prettyNameForLangGetter = Optional.ofNullable(builder.prettyNameForLangGetter)
.orElse(lang -> Optional.empty());
this.depth = (int) getAncestors().count();
Expand Down Expand Up @@ -158,8 +157,7 @@ public int hashCode() {
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof CelTag) {
CelTag other = (CelTag) obj;
} else if (obj instanceof CelTag other) {
return Objects.equals(this.type, other.type)
&& Objects.equals(this.name, other.name)
&& Objects.equals(this.scope, other.scope);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.inject.Inject;

Expand Down Expand Up @@ -48,7 +47,7 @@ public List<CelTag> getTags(String type) {
public List<String> getTagNames(String type) {
return getTags(type).stream()
.map(CelTag::getName)
.collect(Collectors.toList());
.collect(toList());
}

public CelTag getTag(String type, String name) {
Expand Down
87 changes: 70 additions & 17 deletions celements-tag/src/main/java/com/celements/tag/CelTagService.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.celements.tag;

import static com.celements.common.lambda.LambdaExceptionUtil.*;
import static com.google.common.base.Preconditions.*;
import static com.google.common.base.Predicates.*;
import static com.google.common.base.Strings.*;
import static java.util.stream.Collectors.*;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -12,10 +13,12 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Stream;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.slf4j.Logger;
Expand All @@ -24,6 +27,7 @@
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
import org.xwiki.model.reference.EntityReference;

import com.celements.common.lambda.Try;
import com.celements.model.field.XObjectFieldAccessor;
Expand All @@ -32,6 +36,7 @@
import com.celements.tag.classdefs.CelTagClass;
import com.celements.tag.providers.CelTagsProvider;
import com.celements.tag.providers.CelTagsProvider.CelTagsProvisionException;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.xpn.xwiki.doc.XWikiDocument;
Expand Down Expand Up @@ -59,14 +64,27 @@ public CelTagService(

@NotNull
public Optional<CelTag> getTag(@Nullable String type, @Nullable String name) {
return getTagsByType().get(type).stream()
return getTagsByType().get(normaliseType(type)).stream()
.filter(tag -> tag.getName().equals(name))
.findFirst();
}

@NotNull
public Optional<CelTag> getTag(@Nullable String type, @Nullable String name,
@Nullable EntityReference scope) {
return streamTags(type, scope)
.filter(tag -> tag.getName().equals(name))
.findFirst();
}

@NotNull
public StreamEx<CelTag> streamTags(@Nullable String type) {
return StreamEx.of(getTagsByType().get(nullToEmpty(type).toLowerCase()));
return StreamEx.of(getTagsByType().get(normaliseType(type)));
}

@NotNull
public StreamEx<CelTag> streamTags(@Nullable String type, @Nullable EntityReference scope) {
return streamTags(type).filter(tag -> tag.hasScope(scope));
}

@NotNull
Expand Down Expand Up @@ -98,12 +116,12 @@ protected void runInternal() {
}

private Multimap<String, CelTag> collectAllTags() throws CelTagsProvisionException {
List<CelTag.Builder> tagBuilders = beanFactory
List<CelTag.Builder> tagBuilders = new ArrayList<>(beanFactory
.getBeansOfType(CelTagsProvider.class)
.values().stream()
.map(rethrow(CelTagsProvider::get))
.flatMap(Collection::stream)
.collect(toList());
.toList());
LOGGER.info("collectAllTags - {}", tagBuilders);
return topologicalBuild(tagBuilders);
}
Expand Down Expand Up @@ -152,14 +170,15 @@ public StreamEx<CelTag> getDocTags(@NotNull XWikiDocument doc) {
public StreamEx<CelTag> getDocTags(@NotNull XWikiDocument doc, @Nullable String type) {
XWikiObjectFetcher fetcher = XWikiObjectFetcher.on(doc)
.filter(CelTagClass.CLASS_REF);
type = nullToEmpty(type).trim();
type = normaliseType(type);
if (!type.isEmpty()) {
fetcher = fetcher.filter(CelTagClass.FIELD_TYPE, type);
}
return StreamEx.of(fetcher.stream()).flatMap(obj -> getTags(
fieldAccessor.get(obj, CelTagClass.FIELD_TYPE),
fieldAccessor.get(obj, CelTagClass.FIELD_TAGS)
.map(Set::copyOf).orElse(Set.of())));
.map(Set::copyOf).orElse(Set.of())))
.filter(tag -> tag.hasScope(doc.getWikiRef()));
}

private Stream<CelTag> getTags(Optional<String> type, Set<String> tags) {
Expand All @@ -172,20 +191,54 @@ private Stream<CelTag> getTags(Optional<String> type, Set<String> tags) {
@NotNull
public boolean addTags(@NotNull XWikiDocument doc, @NotNull CelTag... tags) {
boolean changed = false;
for (var tagsByType : StreamEx.of(tags).groupingBy(CelTag::getType).entrySet()) {
var editor = XWikiObjectEditor.on(doc)
.filter(CelTagClass.FIELD_TYPE, tagsByType.getKey());
editor.createFirstIfNotExists();
changed |= editor.editField(CelTagClass.FIELD_TAGS)
.all(() -> StreamEx.of(editor.fetch().fetchField(CelTagClass.FIELD_TAGS).stream())
.flatMap(List::stream)
.append(tagsByType.getValue().stream().map(CelTag::getName))
.distinct()
.toList());
var groupedTags = StreamEx.of(tags)
.filter(tag -> tag.hasScope(doc.getWikiRef()))
.groupingBy(CelTag::getType);
for (var tagsByType : groupedTags.entrySet()) {
var type = tagsByType.getKey();
changed |= setTagXObj(doc, type, fetcher -> StreamEx
.of(fetcher.fetchField(CelTagClass.FIELD_TAGS).stream())
.flatMap(List::stream)
.append(tagsByType.getValue().stream().map(CelTag::getName))
.distinct()
.toList());
}
return changed;
}

public boolean setTags(@NotNull XWikiDocument doc, @NotEmpty String type,
@Nullable String... tags) {
var tagType = normaliseType(type);
checkArgument(!tagType.isEmpty(), "tag type cannot be empty");
var tagNames = normaliseTagNames(tags).toSet();
return setTagXObj(doc, tagType, fetcher -> streamTags(tagType, doc.getWikiRef())
.filter(tag -> tagNames.contains(tag.getName()))
.map(CelTag::getName)
.toList());
}

private boolean setTagXObj(XWikiDocument doc, String type,
Function<XWikiObjectFetcher, List<String>> names) {
var editor = XWikiObjectEditor.on(doc)
.filter(CelTagClass.FIELD_TYPE, type);
editor.createFirstIfNotExists();
return editor.editField(CelTagClass.FIELD_TAGS).all(names.apply(editor.fetch()));
}

private String normaliseType(@Nullable String type) {
return nullToEmpty(type).trim().toLowerCase();
}

private StreamEx<String> normaliseTagNames(@Nullable String... tags) {
return StreamEx.ofNullable(tags)
.flatMap(Stream::of)
.map(Strings::nullToEmpty)
.map(String::trim)
.filter(not(String::isEmpty))
.map(String::toLowerCase)
.distinct();
}

@Override
public void onApplicationEvent(RefreshEvent event) {
LOGGER.trace("onApplicationEvent - {}", event);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.celements.tag.controller;

import static java.util.stream.Collectors.*;

import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -17,13 +15,14 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.celements.model.context.ModelContext;
import com.celements.model.util.ModelUtils;
import com.celements.spring.security.AuthenticatedBaseController;
import com.celements.tag.CelTag;
import com.celements.tag.CelTagService;
import com.celements.web.service.IWebUtilsService;
import com.fasterxml.jackson.annotation.JsonInclude;

import one.util.streamex.EntryStream;
import one.util.streamex.StreamEx;

@RestController
Expand All @@ -32,66 +31,87 @@
public class CelTagController extends AuthenticatedBaseController {

private final CelTagService tagService;
private final ModelContext context;
private final ModelUtils modelUtils;
private final IWebUtilsService webUtils;

@Inject
public CelTagController(
CelTagService tagService,
ModelContext context,
ModelUtils modelUtils,
IWebUtilsService webUtils) {
this.tagService = tagService;
this.context = context;
this.modelUtils = modelUtils;
this.webUtils = webUtils;
}

@GetMapping
public Map<String, List<TagDto>> getTags() {
return EntryStream.of(tagService.getTagsByType().entries().stream())
.filterValues(CelTag::isRoot)
.mapValues(TagDto::new)
return tagService.streamAllTags()
.filter(this::isInCurrentScope)
.filter(CelTag::isRoot)
.mapToEntry(CelTag::getType, TagDto::new)
.grouping();
}

@GetMapping("/types")
public Set<String> getTypes() {
return tagService.getTagsByType().keySet();
return tagService.streamAllTags()
.filter(this::isInCurrentScope)
.map(CelTag::getType)
.toSet();
}

@GetMapping("/{type}")
public List<TagDto> getTagsByType(
@PathVariable String type) {
return tagService.getTagsByType().get(type).stream()
return tagService.streamTags(type)
.filter(this::isInCurrentScope)
.filter(CelTag::isRoot)
.sorted(CelTag.CMP_ORDER)
.map(TagDto::new)
.collect(toList());
.toList();
}

@GetMapping("/{type}/{name}")
public ResponseEntity<TagDto> getTagByName(
@PathVariable String type,
@PathVariable String name) {
return tagService.getTag(type, name)
return tagService.streamTags(type)
.filter(this::isInCurrentScope)
.filter(tag -> tag.getName().equals(name))
.findFirst()
.map(TagDto::new)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}

private boolean isInCurrentScope(CelTag tag) {
return modelUtils.isMainWiki(context.getWikiRef()) || tag.hasScope(context.getWikiRef());
}

@Immutable
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class TagDto {

public final String name;
public final String scope;
public final int order;
public final Map<String, String> prettyName;
public final List<TagDto> children;

public TagDto(CelTag tag) {
name = tag.getName();
scope = tag.getScope().map(modelUtils::serializeRef).orElse(null);
order = tag.getOrder();
prettyName = StreamEx.of(webUtils.getAllowedLanguages())
.mapToEntry(tag::getPrettyName)
.flatMapValues(Optional::stream)
.toImmutableMap();
children = tag.getChildren()
.filter(CelTagController.this::isInCurrentScope)
.sorted(CelTag.CMP_ORDER)
.map(TagDto::new)
.toImmutableList();
Expand Down
Loading