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 return !_isStringDataEmpty(parentData, name); 129 } 130 else 131 { 132 return !_isBinaryDataEmpty(parentData, name); 133 } 134 } 135 136 private boolean _isStringDataEmpty(RepositoryData parentData, String name) 137 { 138 if (parentData.isMultiple(name)) 139 { 140 return parentData.getStrings(name).length == 0; 141 } 142 else 143 { 144 String value = parentData.getString(name); 145 return StringUtils.isEmpty(value); 146 } 147 } 148 149 private boolean _isBinaryDataEmpty(RepositoryData parentData, String name) 150 { 151 if (parentData.isMultiple(name)) 152 { 153 RepositoryData multipleParentData = parentData.getRepositoryData(name); 154 return multipleParentData.getDataNames().isEmpty(); 155 } 156 else 157 { 158 RepositoryData data = parentData.getRepositoryData(name); 159 return ResourceElementTypeHelper.isResourceDataEmpty(data); 160 } 161 } 162 163 public void write(ModifiableRepositoryData parentData, String name, Object value) throws BadItemTypeException 164 { 165 if (value == null) 166 { 167 if (parentData.hasValue(name) && parentData.isMultiple(name)) 168 { 169 parentData.setValues(name, new String[0]); 170 } 171 else 172 { 173 parentData.setValue(name, StringUtils.EMPTY); 174 } 175 } 176 else if (value instanceof String) 177 { 178 parentData.setValue(name, (String) value); 179 } 180 else if (value instanceof String[]) 181 { 182 String[] values = Arrays.stream((String[]) value) 183 .map(v -> Optional.ofNullable(v) 184 .orElseThrow(() -> new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"))) 185 .collect(Collectors.toList()) 186 .toArray(new String[((String[]) value).length]); 187 188 parentData.setValues(name, values); 189 } 190 else if (value instanceof ExplorerFile) 191 { 192 writeExplorerFile(parentData, name, (ExplorerFile) value); 193 } 194 else if (value instanceof ExplorerFile[]) 195 { 196 // FIXME : maybe handle the untouched value 197 String[] values = Arrays.stream((ExplorerFile[]) value) 198 .map(file -> Optional.ofNullable(file) 199 .map(ExplorerFile::getResourceId) 200 .orElseThrow(() -> new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"))) 201 .collect(Collectors.toList()) 202 .toArray(new String[((ExplorerFile[]) value).length]); 203 204 parentData.setValues(name, values); 205 } 206 else if (value instanceof Binary) 207 { 208 _writeSingleBinaryValue(parentData, name, (Binary) value); 209 } 210 else if (value instanceof Binary[]) 211 { 212 _writeMultipleBinaryValue(parentData, name, (Binary[]) value); 213 } 214 else 215 { 216 throw new BadItemTypeException("Try to set the non file value '" + value + "' to the file data '" + name + "' on '" + parentData + "'"); 217 } 218 } 219 220 private void writeExplorerFile(ModifiableRepositoryData parentData, String name, ExplorerFile explorerFile) 221 { 222 // FIXME : handle better way the untouched value 223 String resourceId = explorerFile.getResourceId(); 224 if (!"untouched".equals(resourceId)) 225 { 226 parentData.setValue(name, resourceId); 227 } 228 } 229 230 private void _writeMultipleBinaryValue(ModifiableRepositoryData parentData, String name, Binary[] value) 231 { 232 ModifiableRepositoryData multipleParentData; 233 234 if (parentData.hasValue(name)) 235 { 236 multipleParentData = parentData.getRepositoryData(name); 237 } 238 else 239 { 240 multipleParentData = parentData.addRepositoryData(name, RepositoryConstants.MULTIPLE_ITEM_NODETYPE); 241 // Add the type identifier in a property to know what the type of the data even if there is no value 242 multipleParentData.setValue(ComplexRepositoryElementType.TYPE_ID_DATA_NAME, getId(), RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 243 } 244 245 for (int i = 0; i < value.length; i++) 246 { 247 if (value[i] != null) 248 { 249 _writeSingleBinaryValue(multipleParentData, String.valueOf(i + 1), value[i]); 250 } 251 else 252 { 253 throw new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"); 254 } 255 } 256 } 257 258 private void _writeSingleBinaryValue(ModifiableRepositoryData parentData, String name, Binary binary) 259 { 260 if (parentData.hasValue(name)) 261 { 262 String dataType = parentData.getType(name); 263 if (dataType.equals(RepositoryData.STRING_REPOSITORY_DATA_TYPE)) 264 { 265 // In this case, there are a explorer data (type String) in the repo, so delete it before adding the binary data 266 parentData.removeValue(name); 267 } 268 } 269 270 ResourceElementTypeHelper.writeSingleBinaryValue(parentData, name, binary); 271 } 272 273 public String getRepositoryDataType() 274 { 275 return RepositoryData.STRING_REPOSITORY_DATA_TYPE; 276 } 277 278 public boolean isCompatible(RepositoryData parentData, String name) throws UnknownDataException 279 { 280 if (parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)) 281 { 282 return true; 283 } 284 285 String dataType = parentData.getType(name); 286 switch (dataType) 287 { 288 case RepositoryData.STRING_REPOSITORY_DATA_TYPE: 289 case RepositoryConstants.BINARY_NODETYPE: 290 return true; 291 case RepositoryConstants.MULTIPLE_ITEM_NODETYPE: 292 RepositoryData multipleItemNode = parentData.getRepositoryData(name); 293 return getId().equals(multipleItemNode.getString(ComplexRepositoryElementType.TYPE_ID_DATA_NAME, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)); 294 default: 295 return false; 296 } 297 } 298}