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.cms.data.type.impl; 017 018import java.util.Arrays; 019import java.util.Optional; 020import java.util.stream.Collectors; 021 022import javax.jcr.Session; 023 024import org.apache.commons.lang3.StringUtils; 025 026import org.ametys.cms.data.Binary; 027import org.ametys.cms.data.ExplorerFile; 028import org.ametys.cms.data.File; 029import org.ametys.cms.data.type.AbstractFileElementType; 030import org.ametys.cms.data.type.ResourceElementTypeHelper; 031import org.ametys.plugins.repository.AmetysRepositoryException; 032import org.ametys.plugins.repository.RepositoryConstants; 033import org.ametys.plugins.repository.data.UnknownDataException; 034import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 035import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 036import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData; 037import org.ametys.plugins.repository.data.type.ComplexRepositoryElementType; 038import org.ametys.plugins.repository.data.type.RepositoryElementType; 039import org.ametys.runtime.model.exception.BadItemTypeException; 040 041/** 042 * Class for file type of elements stored in the repository 043 */ 044public class FileRepositoryElementType extends AbstractFileElementType implements RepositoryElementType<File> 045{ 046 public Object read(RepositoryData parentData, String name) throws BadItemTypeException, AmetysRepositoryException 047 { 048 if (!parentData.hasValue(name)) 049 { 050 return null; 051 } 052 053 if (!isCompatible(parentData, name)) 054 { 055 throw new BadItemTypeException("Try to get file value from the non file data '" + name + "' on '" + parentData + "'"); 056 } 057 058 if (RepositoryData.STRING_REPOSITORY_DATA_TYPE.equals(parentData.getType(name))) 059 { 060 return _readFileFromStringData(parentData, name); 061 } 062 else 063 { 064 return _readFileFromBinaryData(parentData, name); 065 } 066 } 067 068 private Object _readFileFromStringData(RepositoryData parentData, String name) 069 { 070 final Session session = parentData instanceof JCRRepositoryData ? ((JCRRepositoryData) parentData).getSession() : null; 071 072 if (parentData.isMultiple(name)) 073 { 074 String[] values = parentData.getStrings(name); 075 return Arrays.stream(values) 076 .map(resourceId -> new ExplorerFile(_resolver, resourceId, session)) 077 .collect(Collectors.toList()) 078 .toArray(new ExplorerFile[values.length]); 079 } 080 else 081 { 082 String value = parentData.getString(name); 083 return StringUtils.isNotEmpty(value) ? new ExplorerFile(_resolver, value, session) : null; 084 } 085 } 086 087 private Object _readFileFromBinaryData(RepositoryData parentData, String name) 088 { 089 if (parentData.isMultiple(name)) 090 { 091 RepositoryData multipleParentData = parentData.getRepositoryData(name); 092 093 return multipleParentData.getDataNames() 094 .stream() 095 .map(singleDataName -> multipleParentData.getRepositoryData(singleDataName)) 096 .map(singleData -> ResourceElementTypeHelper.readBinaryData(singleData)) 097 .filter(singleValue -> singleValue != null) 098 .toArray(Binary[]::new); 099 } 100 else 101 { 102 RepositoryData data = parentData.getRepositoryData(name); 103 if (!ResourceElementTypeHelper.isResourceDataEmpty(data)) 104 { 105 return ResourceElementTypeHelper.readBinaryData(data); 106 } 107 else 108 { 109 return null; 110 } 111 } 112 } 113 114 public boolean hasNonEmptyValue(RepositoryData parentData, String name) throws BadItemTypeException 115 { 116 if (!parentData.hasValue(name)) 117 { 118 return false; 119 } 120 121 if (!isCompatible(parentData, name)) 122 { 123 throw new BadItemTypeException("Try to check file value from the non file data '" + name + "' on '" + parentData + "'"); 124 } 125 126 if (RepositoryData.STRING_REPOSITORY_DATA_TYPE.equals(parentData.getType(name))) 127 { 128 if (parentData.isMultiple(name)) 129 { 130 return parentData.getStrings(name).length > 0; 131 } 132 else 133 { 134 return StringUtils.isNotEmpty(parentData.getString(name)); 135 } 136 } 137 else 138 { 139 // There is a non string value, it cannot be empty 140 return true; 141 } 142 } 143 144 public void write(ModifiableRepositoryData parentData, String name, Object value) throws BadItemTypeException 145 { 146 if (value == null) 147 { 148 if (parentData.hasValue(name) && parentData.isMultiple(name)) 149 { 150 parentData.setValues(name, new String[0]); 151 } 152 else 153 { 154 parentData.setValue(name, StringUtils.EMPTY); 155 } 156 } 157 else if (value instanceof String) 158 { 159 parentData.setValue(name, (String) value); 160 } 161 else if (value instanceof String[]) 162 { 163 String[] values = Arrays.stream((String[]) value) 164 .map(v -> Optional.ofNullable(v) 165 .orElseThrow(() -> new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"))) 166 .collect(Collectors.toList()) 167 .toArray(new String[((String[]) value).length]); 168 169 parentData.setValues(name, values); 170 } 171 else if (value instanceof ExplorerFile) 172 { 173 writeExplorerFile(parentData, name, (ExplorerFile) value); 174 } 175 else if (value instanceof Binary) 176 { 177 _writeSingleBinaryValue(parentData, name, (Binary) value); 178 } 179 else if (value instanceof File[]) 180 { 181 if (((File[]) value).length == 0) 182 { 183 parentData.setValues(name, new String[0]); 184 } 185 else if (value instanceof ExplorerFile[]) 186 { 187 // FIXME : maybe handle the untouched value 188 String[] values = Arrays.stream((ExplorerFile[]) value) 189 .map(file -> Optional.ofNullable(file) 190 .map(ExplorerFile::getResourceId) 191 .orElseThrow(() -> new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"))) 192 .collect(Collectors.toList()) 193 .toArray(new String[((ExplorerFile[]) value).length]); 194 195 parentData.setValues(name, values); 196 } 197 else if (value instanceof Binary[]) 198 { 199 _writeMultipleBinaryValue(parentData, name, (Binary[]) value); 200 } 201 } 202 else 203 { 204 throw new BadItemTypeException("Try to set the non file value '" + value + "' to the file data '" + name + "' on '" + parentData + "'"); 205 } 206 } 207 208 private void writeExplorerFile(ModifiableRepositoryData parentData, String name, ExplorerFile explorerFile) 209 { 210 // FIXME : handle better way the untouched value 211 String resourceId = explorerFile.getResourceId(); 212 if (!"untouched".equals(resourceId)) 213 { 214 parentData.setValue(name, resourceId); 215 } 216 } 217 218 private void _writeMultipleBinaryValue(ModifiableRepositoryData parentData, String name, Binary[] value) 219 { 220 ModifiableRepositoryData multipleParentData = null; 221 if (parentData.hasValue(name)) 222 { 223 if (parentData.getType(name).equals(RepositoryConstants.MULTIPLE_ITEM_NODETYPE)) 224 { 225 multipleParentData = parentData.getRepositoryData(name); 226 } 227 else 228 { 229 // The value was stored as String, remove the old property 230 parentData.removeValue(name); 231 } 232 } 233 234 if (multipleParentData == null) 235 { 236 multipleParentData = parentData.addRepositoryData(name, RepositoryConstants.MULTIPLE_ITEM_NODETYPE); 237 // Add the type identifier in a property to know what the type of the data even if there is no value 238 multipleParentData.setValue(ComplexRepositoryElementType.TYPE_ID_DATA_NAME, getId(), RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 239 } 240 241 // Loop on all given values to replace the existing ones 242 for (int i = 0; i < value.length; i++) 243 { 244 if (value[i] != null) 245 { 246 _writeSingleBinaryValue(multipleParentData, String.valueOf(i + 1), value[i]); 247 } 248 else 249 { 250 throw new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"); 251 } 252 } 253 254 // Remove existing entries from the last one to the last of given values 255 for (int i = multipleParentData.getDataNames().size(); i > value.length; i--) 256 { 257 multipleParentData.removeValue(String.valueOf(i)); 258 } 259 } 260 261 private void _writeSingleBinaryValue(ModifiableRepositoryData parentData, String name, Binary binary) 262 { 263 if (parentData.hasValue(name)) 264 { 265 String dataType = parentData.getType(name); 266 if (dataType.equals(RepositoryData.STRING_REPOSITORY_DATA_TYPE)) 267 { 268 // In this case, there are a explorer data (type String) in the repo, so delete it before adding the binary data 269 parentData.removeValue(name); 270 } 271 } 272 273 ResourceElementTypeHelper.writeSingleBinaryValue(parentData, name, binary); 274 } 275 276 public String getRepositoryDataType() 277 { 278 return RepositoryData.STRING_REPOSITORY_DATA_TYPE; 279 } 280 281 public boolean isCompatible(RepositoryData parentData, String name) throws UnknownDataException 282 { 283 if (parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)) 284 { 285 return true; 286 } 287 288 String dataType = parentData.getType(name); 289 switch (dataType) 290 { 291 case RepositoryData.STRING_REPOSITORY_DATA_TYPE: 292 case RepositoryConstants.BINARY_NODETYPE: 293 return true; 294 case RepositoryConstants.MULTIPLE_ITEM_NODETYPE: 295 RepositoryData multipleItemNode = parentData.getRepositoryData(name); 296 return getId().equals(multipleItemNode.getString(ComplexRepositoryElementType.TYPE_ID_DATA_NAME, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)); 297 default: 298 return false; 299 } 300 } 301}