001/* 002 * Copyright 2019 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.odf.catalog; 017 018import java.util.Map; 019import java.util.Objects; 020import java.util.Optional; 021import java.util.stream.Stream; 022 023import org.apache.commons.lang3.StringUtils; 024 025import org.ametys.cms.contenttype.ContentAttributeDefinition; 026import org.ametys.cms.data.ContentValue; 027import org.ametys.cms.repository.Content; 028import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder; 029import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareComposite; 030import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareRepeater; 031import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareRepeaterEntry; 032import org.ametys.plugins.repository.model.CompositeDefinition; 033import org.ametys.plugins.repository.model.RepeaterDefinition; 034import org.ametys.runtime.model.ModelItem; 035import org.ametys.runtime.plugin.component.AbstractLogEnabled; 036 037/** 038 * The abstract class to copy content attribute of program item type. 039 */ 040public abstract class AbstractProgramItemAttributeCopyUpdater extends AbstractLogEnabled implements CopyCatalogUpdater 041{ 042 /** 043 * Update the program item attribute with the value of the new catalog. 044 * @param dataHolder The data holder 045 * @param definitionPath The definition path 046 * @param copiedContents The copied contents for the referenced attribute 047 */ 048 protected void _updateContentAttribute(ModifiableModelAwareDataHolder dataHolder, String definitionPath, Map<Content, Content> copiedContents) 049 { 050 String[] pathSegments = StringUtils.split(definitionPath, ModelItem.ITEM_PATH_SEPARATOR); 051 String attributeName = pathSegments[0]; 052 ModelItem definition = dataHolder.getDefinition(attributeName); 053 if (pathSegments.length == 1 && definition instanceof ContentAttributeDefinition) 054 { 055 if (((ContentAttributeDefinition) definition).isMultiple()) 056 { 057 _updateMultipleContentAttribute(dataHolder, attributeName, copiedContents); 058 } 059 else 060 { 061 _updateSingleContentAttribute(dataHolder, attributeName, copiedContents); 062 } 063 } 064 else if (definition instanceof RepeaterDefinition) 065 { 066 ModifiableModelAwareRepeater repeater = dataHolder.getRepeater(attributeName); 067 if (repeater != null) 068 { 069 String childDefinitionPath = StringUtils.join(pathSegments, ModelItem.ITEM_PATH_SEPARATOR, 1, pathSegments.length); 070 for (ModifiableModelAwareRepeaterEntry entry : repeater.getEntries()) 071 { 072 _updateContentAttribute(entry, childDefinitionPath, copiedContents); 073 } 074 } 075 } 076 else if (definition instanceof CompositeDefinition) 077 { 078 ModifiableModelAwareComposite composite = dataHolder.getComposite(attributeName); 079 if (composite != null) 080 { 081 String childDefinitionPath = StringUtils.join(pathSegments, ModelItem.ITEM_PATH_SEPARATOR, 1, pathSegments.length); 082 _updateContentAttribute(composite, childDefinitionPath, copiedContents); 083 } 084 } 085 else 086 { 087 // This method shouldn't be called with anything else than a final content attribute 088 throw new IllegalArgumentException("The path '" + definitionPath + "' on data holder '" + definition.getPath() + "' is not a content attribute on content type '" + definition.getModel().getId() + "'."); 089 } 090 } 091 092 /** 093 * Update the single attribute with the content value. 094 * @param dataHolder The data holder 095 * @param attributeName The attribute name 096 * @param copiedContents The copied contents for the referenced attribute 097 */ 098 protected void _updateSingleContentAttribute(ModifiableModelAwareDataHolder dataHolder, String attributeName, Map<Content, Content> copiedContents) 099 { 100 Content newContent = Optional.of(dataHolder) 101 // Get the attribute value 102 .map(dh -> dh.<ContentValue>getValue(attributeName)) 103 // Get the copied content 104 .map(initialValue -> _getValueFromCopiedContents(initialValue, copiedContents)) 105 // Get the retrieved value or null 106 .orElse(null); 107 108 // Set the attribute with the new content 109 // If null, the attribute value will be empty 110 dataHolder.setValue(attributeName, newContent); 111 } 112 113 /** 114 * Update the multiple attribute with the program item value. 115 * @param dataHolder The data holder 116 * @param attributeName The attribute name 117 * @param copiedContents The copied contents for the referenced attribute 118 */ 119 protected void _updateMultipleContentAttribute(ModifiableModelAwareDataHolder dataHolder, String attributeName, Map<Content, Content> copiedContents) 120 { 121 Content[] newContents = Optional.of(dataHolder) 122 // Get the attribute values 123 .map(dh -> dh.<ContentValue[]>getValue(attributeName)) 124 // Build a stream from the ContentValue array 125 .map(Stream::of) 126 // Or build an empty stream if there is no values 127 .orElseGet(Stream::empty) 128 // For each element, get the copied content 129 .map(initialValue -> _getValueFromCopiedContents(initialValue, copiedContents)) 130 .filter(Objects::nonNull) 131 // Collect into an array 132 .toArray(Content[]::new); 133 134 // Set the attribute with the new contents list 135 dataHolder.setValue(attributeName, newContents); 136 } 137 138 private Content _getValueFromCopiedContents(ContentValue initialContent, Map<Content, Content> copiedContents) 139 { 140 Content copiedContent = initialContent.getContentIfExists() 141 // Keep himself if not found in the copied contents map 142 .map(content -> copiedContents.getOrDefault(content, content)) 143 .orElse(null); 144 145 if (getLogger().isDebugEnabled()) 146 { 147 if (copiedContent == null) 148 { 149 getLogger().debug("The initial content '{}' does not exists anymore, it cannot match a copied content.", initialContent.getContentId()); 150 } 151 else if (copiedContent.getId().equals(initialContent.getContentId())) 152 { 153 getLogger().debug("The initial content '{}' does not match any copied content.", initialContent.getContentId()); 154 } 155 } 156 157 return copiedContent; 158 } 159}