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; 017 018import java.time.ZoneId; 019import java.time.ZonedDateTime; 020import java.time.format.DateTimeFormatter; 021import java.util.Calendar; 022import java.util.GregorianCalendar; 023import java.util.LinkedHashMap; 024import java.util.Map; 025import java.util.Optional; 026 027import org.apache.cocoon.xml.AttributesImpl; 028import org.apache.cocoon.xml.XMLUtils; 029import org.xml.sax.ContentHandler; 030import org.xml.sax.SAXException; 031 032import org.ametys.cms.data.Binary; 033import org.ametys.cms.data.File; 034import org.ametys.cms.data.Resource; 035import org.ametys.plugins.repository.RepositoryConstants; 036import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 037import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 038import org.ametys.runtime.model.exception.BadItemTypeException; 039 040/** 041 * Helper for resource type of elements stored in the repository 042 */ 043public final class ResourceElementTypeHelper 044{ 045 /** Prefix to use for resource's metadata */ 046 public static final String METADATA_PREFIX = "jcr"; 047 048 /** Mime type metadata identifier */ 049 public static final String MIME_TYPE_IDENTIFIER = "mimeType"; 050 051 /** Encoding metadata identifier */ 052 public static final String ENCODING_IDENTIFIER = "encoding"; 053 054 /** Last modification date metadata identifier */ 055 public static final String LAST_MODIFICATION_DATE_IDENTIFIER = "lastModified"; 056 057 /** Identifier of the data containing the resource's data */ 058 public static final String DATA_IDENTIFIER = "data"; 059 060 /** hash metadata identifier */ 061 public static final String HASH_IDENTIFIER = "hash"; 062 063 /** file name metadata identifier */ 064 public static final String FILENAME_IDENTIFIER = "filename"; 065 066 private ResourceElementTypeHelper() 067 { 068 // Empty constructor 069 } 070 071 /** 072 * Generates SAX events for the given single file 073 * @param contentHandler the {@link ContentHandler} that will receive the SAX events 074 * @param tagName the tag name of the SAX event to generate. 075 * @param file the single file to SAX 076 * @param fileType the type of the file 077 * @throws SAXException if an error occurs during the SAX events generation 078 */ 079 public static void singleFileToSAX(ContentHandler contentHandler, String tagName, File file, String fileType) throws SAXException 080 { 081 AttributesImpl attributes = new AttributesImpl(); 082 083 Optional.ofNullable(file.getMimeType()).ifPresent(mimeType -> attributes.addCDATAAttribute("mime-type", mimeType)); 084 Optional.ofNullable(file.getLastModificationDate()).ifPresent(lastModificationDate -> attributes.addCDATAAttribute("lastModified", lastModificationDate.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); 085 086 attributes.addCDATAAttribute("type", fileType); 087 attributes.addCDATAAttribute("size", String.valueOf(file.getLength())); 088 Optional.ofNullable(file.getPath()).ifPresent(path -> attributes.addCDATAAttribute("path", path)); 089 Optional.ofNullable(file.getName()).ifPresent(filename -> attributes.addCDATAAttribute("filename", filename)); 090 091 XMLUtils.createElement(contentHandler, tagName, attributes); 092 } 093 094 /** 095 * Convert the single file into a JSON object 096 * @param file the file to convert 097 * @param fileType the type of the file 098 * @return The file as JSON 099 */ 100 public static Object singleFileToJSON(File file, String fileType) 101 { 102 Map<String, Object> binaryInfos = new LinkedHashMap<>(); 103 104 Optional.ofNullable(file.getMimeType()).ifPresent(mimeType -> binaryInfos.put("mime-type", mimeType)); 105 Optional.ofNullable(file.getLastModificationDate()).ifPresent(lastModificationDate -> binaryInfos.put("lastModified", lastModificationDate.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); 106 107 binaryInfos.put("size", String.valueOf(file.getLength())); 108 Optional.ofNullable(file.getPath()).ifPresent(path -> binaryInfos.put("path", path)); 109 binaryInfos.put("type", fileType); 110 Optional.ofNullable(file.getName()).ifPresent(filename -> binaryInfos.put("filename", filename)); 111 112 return binaryInfos; 113 } 114 115 /** 116 * Read the binary from the given repository data 117 * @param binaryData the repository data containing the binary's data 118 * @return the read binary 119 */ 120 public static Binary readBinaryData(RepositoryData binaryData) 121 { 122 String hash = getStringValue(binaryData, HASH_IDENTIFIER, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 123 Binary binary = new Binary(binaryData, hash); 124 125 readResourceData(binaryData, binary); 126 Optional.ofNullable(getStringValue(binaryData, FILENAME_IDENTIFIER, RepositoryConstants.NAMESPACE_PREFIX)).ifPresent(binary::setFilename); 127 128 return binary; 129 } 130 131 /** 132 * Read the resource from the given repository data 133 * @param resourceData the repository data containing the resource's data 134 * @param resource the resource to read 135 */ 136 public static void readResourceData(RepositoryData resourceData, Resource resource) 137 { 138 Optional.ofNullable(getStringValue(resourceData, MIME_TYPE_IDENTIFIER, METADATA_PREFIX)).ifPresent(resource::setMimeType); 139 Optional.ofNullable(getStringValue(resourceData, ENCODING_IDENTIFIER, METADATA_PREFIX)).ifPresent(resource::setEncoding); 140 Optional.ofNullable(getDateValue(resourceData, LAST_MODIFICATION_DATE_IDENTIFIER, METADATA_PREFIX)).ifPresent(resource::setLastModificationDate); 141 } 142 143 /** 144 * Write the resource in the given repository data 145 * @param parentData the repository data where to write the binary 146 * @param name the name of the element to write 147 * @param value the binary to write 148 */ 149 public static void writeSingleBinaryValue(ModifiableRepositoryData parentData, String name, Binary value) 150 { 151 ModifiableRepositoryData binaryData; 152 if (parentData.hasValue(name)) 153 { 154 binaryData = getRepositoryData(parentData, RepositoryConstants.BINARY_NODETYPE, name, RepositoryConstants.NAMESPACE_PREFIX); 155 } 156 else 157 { 158 binaryData = parentData.addRepositoryData(name, RepositoryConstants.BINARY_NODETYPE); 159 } 160 161 if (value != null) 162 { 163 writeResourceData(binaryData, value); 164 Optional.ofNullable(value.getFilename()).ifPresent(filename -> binaryData.setValue(FILENAME_IDENTIFIER, filename, RepositoryConstants.NAMESPACE_PREFIX)); 165 166 if (binaryData.hasValue(HASH_IDENTIFIER, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)) 167 { 168 String currentHash = getStringValue(binaryData, HASH_IDENTIFIER, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 169 if (!currentHash.equals(value.getHash())) 170 { 171 Optional.ofNullable(value.getHash()).ifPresent(hash -> binaryData.setValue(HASH_IDENTIFIER, hash, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)); 172 Optional.ofNullable(value.getInputStream()).ifPresent(stream -> binaryData.setValue(DATA_IDENTIFIER, stream, METADATA_PREFIX)); 173 } 174 } 175 else 176 { 177 Optional.ofNullable(value.getHash()).ifPresent(hash -> binaryData.setValue(HASH_IDENTIFIER, hash, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)); 178 Optional.ofNullable(value.getInputStream()).ifPresent(stream -> binaryData.setValue(DATA_IDENTIFIER, stream, METADATA_PREFIX)); 179 } 180 } 181 } 182 183 /** 184 * Write the resource in the given repository data 185 * @param resourceData the repository data where to write the resource's data 186 * @param value the resource to write 187 */ 188 public static void writeResourceData(ModifiableRepositoryData resourceData, Resource value) 189 { 190 Optional.ofNullable(value.getMimeType()).ifPresent(mimeType -> resourceData.setValue(MIME_TYPE_IDENTIFIER, mimeType, METADATA_PREFIX)); 191 Optional.ofNullable(value.getEncoding()).ifPresent(encoding -> resourceData.setValue(ENCODING_IDENTIFIER, encoding, METADATA_PREFIX)); 192 Optional.ofNullable(value.getLastModificationDate()).ifPresent(lastModificationDate -> resourceData.setValue(LAST_MODIFICATION_DATE_IDENTIFIER, _getCalendarFromZonedDateTime(lastModificationDate), METADATA_PREFIX)); 193 } 194 195 /** 196 * Retrieves the string value from the given repository data 197 * @param repositoryData the repository data containing the data to retrieve 198 * @param name the name of the data to retrieve 199 * @param prefix the prefix of the data to retrieve 200 * @return the string value 201 */ 202 public static String getStringValue(RepositoryData repositoryData, String name, String prefix) 203 { 204 if (!repositoryData.hasValue(name, prefix)) 205 { 206 return null; 207 } 208 else if (RepositoryData.STRING_REPOSITORY_DATA_TYPE.equals(repositoryData.getType(name, prefix))) 209 { 210 return repositoryData.getString(name, prefix); 211 } 212 else 213 { 214 throw new BadItemTypeException("Try to get string value from the non string data '" + prefix + ":" + name + "' on '" + repositoryData + "'"); 215 } 216 } 217 218 /** 219 * Retrieves the string values from the given repository data 220 * @param repositoryData the repository data containing the data to retrieve 221 * @param name the name of the data to retrieve 222 * @param prefix the prefix of the data to retrieve 223 * @return the string values 224 */ 225 public static String[] getStringValues(RepositoryData repositoryData, String name, String prefix) 226 { 227 if (!repositoryData.hasValue(name, prefix)) 228 { 229 return null; 230 } 231 else if (RepositoryData.STRING_REPOSITORY_DATA_TYPE.equals(repositoryData.getType(name, prefix))) 232 { 233 return repositoryData.getStrings(name, prefix); 234 } 235 else 236 { 237 throw new BadItemTypeException("Try to get string value from the non string data '" + prefix + ":" + name + "' on '" + repositoryData + "'"); 238 } 239 } 240 241 /** 242 * Retrieves the child repository data from the given repository data 243 * @param <T> Type of the repository data (modifiable or not) 244 * @param parentData the repository data containing the data to retrieve 245 * @param dataTypeName the type of the data to retrieve 246 * @param name the name of the data to retrieve 247 * @param prefix the prefix of the data to retrieve 248 * @return the child repository data 249 */ 250 @SuppressWarnings("unchecked") 251 public static <T extends RepositoryData> T getRepositoryData(T parentData, String dataTypeName, String name, String prefix) 252 { 253 if (!parentData.hasValue(name, prefix)) 254 { 255 return null; 256 } 257 else if (dataTypeName.equals(parentData.getType(name, prefix))) 258 { 259 return (T) parentData.getRepositoryData(name, prefix); 260 } 261 else 262 { 263 throw new BadItemTypeException("Try to get data of type '" + dataTypeName + "' from the non data '" + prefix + ":" + name + "' on '" + parentData + "'"); 264 } 265 } 266 267 /** 268 * Retrieves the date value from the given repository data 269 * @param repositoryData the repository data containing data to retrieve 270 * @param prefix the prefix of the data to retrieve 271 * @param name the name of the data to retrieve 272 * @return the date value 273 */ 274 public static ZonedDateTime getDateValue(RepositoryData repositoryData, String name, String prefix) 275 { 276 if (!repositoryData.hasValue(name, prefix)) 277 { 278 return null; 279 } 280 else if (RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE.equals(repositoryData.getType(name, prefix))) 281 { 282 return _getZonedDateTimeFromCalendar(repositoryData.getDate(name, prefix)); 283 } 284 else 285 { 286 throw new BadItemTypeException("Try to get date value from the non date data '" + prefix + ":" + name + "' on '" + repositoryData + "'"); 287 } 288 } 289 290 private static ZonedDateTime _getZonedDateTimeFromCalendar(Calendar calendar) 291 { 292 ZonedDateTime zonedDateTime = ZonedDateTime.of(calendar.get(Calendar.YEAR), 293 calendar.get(Calendar.MONTH) + 1, 294 calendar.get(Calendar.DAY_OF_MONTH), 295 calendar.get(Calendar.HOUR_OF_DAY), 296 calendar.get(Calendar.MINUTE), 297 calendar.get(Calendar.SECOND), 298 calendar.get(Calendar.MILLISECOND), 299 calendar.getTimeZone().toZoneId()); 300 return zonedDateTime.withZoneSameInstant(ZoneId.of("UTC")); 301 302 } 303 304 private static Calendar _getCalendarFromZonedDateTime(ZonedDateTime zonedDateTime) 305 { 306 ZonedDateTime dateTimeOnUTC = zonedDateTime.withZoneSameInstant(ZoneId.of("UTC")); 307 return GregorianCalendar.from(dateTimeOnUTC); 308 } 309}