com.exadel.aem.toolkit.plugin.handlers.common.AllowedChildrenHandler Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.exadel.aem.toolkit.plugin.handlers.common;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.exadel.aem.toolkit.api.annotations.meta.Scopes;
import com.exadel.aem.toolkit.api.annotations.policies.AllowedChildren;
import com.exadel.aem.toolkit.api.annotations.policies.PolicyMergeMode;
import com.exadel.aem.toolkit.api.annotations.policies.PolicyTarget;
import com.exadel.aem.toolkit.api.handlers.Handler;
import com.exadel.aem.toolkit.api.handlers.Source;
import com.exadel.aem.toolkit.api.handlers.Target;
import com.exadel.aem.toolkit.core.CoreConstants;
import com.exadel.aem.toolkit.plugin.maven.PluginRuntime;
import com.exadel.aem.toolkit.plugin.metadata.Metadata;
import com.exadel.aem.toolkit.plugin.utils.ArrayUtil;
import com.exadel.aem.toolkit.plugin.utils.DialogConstants;
/**
* Implements {@code BiConsumer} to populate a {@link Target} instance with properties originating from a {@link Source}
* object. The source refers to the {@code updatecomponentlist} listener of the {@code cq:childEditConfig} or {@code
* cq:editConfig} node of an AEM component
*/
public class AllowedChildrenHandler implements Handler {
private static final String VALUE_POLICY_RESOLVER_FORMAT = "Granite.PolicyResolver.build('%s')";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.registerModule(new SimpleModule()
.addSerializer(AllowedChildren.class, new AllowedChildrenSerializer()));
/**
* Processes data that can be extracted from the given {@code Source} and stores it into the provided {@code
* Target}
* @param source {@code Source} object used for data retrieval
* @param target Resulting {@code Target} object
*/
@Override
public void accept(Source source, Target target) {
source.tryAdaptTo(AllowedChildren[].class).ifPresent(adaptation -> populatePolicies(adaptation, target));
}
/**
* Processes data from {@code AllowedChildren[]} annotations and stores it into 'cq:listeners' node of the provided
* {@code Target}
* @param rules {@code AllowedChildren[]} object used for data retrieval
* @param target Resulting {@code Target} object
*/
private static void populatePolicies(AllowedChildren[] rules, Target target) {
List allowedChildrenList = Arrays.stream(rules)
.filter(rule -> isEditConfig(target) == (PolicyTarget.CURRENT == rule.targetContainer()))
.map(AllowedChildrenHandler::combineValues)
.collect(Collectors.toList());
if (allowedChildrenList.isEmpty()) {
return;
}
String json = toJson(allowedChildrenList, isEditConfig(target))
.replace("'", "\\\\'");
target
.attribute(DialogConstants.PN_PRIMARY_TYPE, DialogConstants.NT_EDIT_CONFIG)
.getOrCreateTarget(DialogConstants.NN_LISTENERS)
.attribute(DialogConstants.PN_PRIMARY_TYPE, DialogConstants.NT_LISTENERS)
.attribute(CoreConstants.PN_UPDATE_COMPONENT_LIST, String.format(VALUE_POLICY_RESOLVER_FORMAT, json));
}
/**
* Gets whether the given {@link Target} is a representation of a {@code cq:editConfig} node of an AEM component
* @param target {@code Target} instance
* @return True or false
*/
private static boolean isEditConfig(Target target) {
return Scopes.CQ_EDIT_CONFIG.equals(target.getScope());
}
/**
* Called from {@link AllowedChildrenHandler#populatePolicies(AllowedChildren[], Target)} to combine
* the rule's values (i.e., components that are referenced either by path or by class)
* @param rule {@link AllowedChildren} instance
* @return Modified {@link AllowedChildren} instance
*/
private static AllowedChildren combineValues(AllowedChildren rule) {
if (ArrayUtils.isEmpty(rule.classes())) {
return rule;
}
List effectiveValues = new ArrayList<>(Arrays.asList(rule.value()));
Arrays.stream(rule.classes())
.map(cls -> PluginRuntime.context().getReflection().getComponent(cls).getJcrPath())
.filter(StringUtils::isNotBlank)
.forEach(effectiveValues::add);
Map modification = Collections.singletonMap(
CoreConstants.PN_VALUE,
effectiveValues.stream().distinct().toArray(String[]::new));
return (AllowedChildren) Metadata.from(rule, modification);
}
/* -------------
Serialization
------------- */
/**
* Converts {@code List} of {@link AllowedChildren} annotations and boolean value representing target node to the
* JSON format
* @param rules {@code List} of {@link AllowedChildren} annotations
* @param isEditConfig True indicates that the listener specified in {@code cq:editConfig} is used. Otherwise, the
* listener in {@code cq:childEditConfig} is used
* @return True or false
*/
private static String toJson(List rules, boolean isEditConfig) {
ObjectNode objectNode = OBJECT_MAPPER.createObjectNode();
objectNode.put("isEditConfig", isEditConfig);
objectNode.set("rules", OBJECT_MAPPER.valueToTree(rules));
return objectNode.toString();
}
/**
* Represents {@link JsonSerializer} for storing the configuration set up via {@link AllowedChildren} in the content
* repository
*/
private static class AllowedChildrenSerializer extends JsonSerializer {
/**
* Retrieves a JSON render of the provided {@code AllowedChildren} annotation
* @param allowedChildren {@code AllowedChildren} object
* @param jsonGenerator Managed {@code JsonGenerator} object
* @param serializerProvider Managed {@code SerializerProvider} object
* @throws IOException if the serialization fails
*/
@Override
public void serialize(
AllowedChildren allowedChildren,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
serializeNonEmptyArray(
"value",
ArrayUtil.flatten(allowedChildren.value()),
jsonGenerator,
serializerProvider);
serializeNonEmptyArray(
"pageResourceTypes",
ArrayUtil.flatten(allowedChildren.pageResourceTypes()),
jsonGenerator,
serializerProvider);
serializeNonEmptyArray(
"templates",
ArrayUtil.flatten(allowedChildren.templates()),
jsonGenerator,
serializerProvider);
serializeNonEmptyArray(
"parentsResourceTypes",
allowedChildren.parents(),
jsonGenerator,
serializerProvider);
serializeNonEmptyArray(
"pagePaths",
ArrayUtil.flatten(allowedChildren.pagePaths()),
jsonGenerator,
serializerProvider);
serializeNonEmptyArray(
"containers",
ArrayUtil.flatten(allowedChildren.resourceNames()),
jsonGenerator,
serializerProvider);
if (PolicyMergeMode.OVERRIDE != allowedChildren.mode()) {
serializerProvider.defaultSerializeField("mode", allowedChildren.mode(), jsonGenerator);
}
jsonGenerator.writeEndObject();
}
/**
* Retrieves a JSON render of the provided array
* @param fieldName Name of the field being serialized
* @param value Value of the field
* @param jsonGenerator Managed {@code JsonGenerator} object
* @param serializerProvider Managed {@code SerializerProvider} object
* @throws IOException if the serialization fails
*/
private void serializeNonEmptyArray(
String fieldName,
String[] value,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
if (value.length == 0) {
return;
}
serializerProvider.defaultSerializeField(fieldName, value, jsonGenerator);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy