001/* 002 * Copyright 2018 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.io.IOException; 019import java.time.LocalDate; 020import java.time.ZoneId; 021import java.time.ZonedDateTime; 022import java.util.Arrays; 023import java.util.Calendar; 024import java.util.Optional; 025 026import javax.xml.transform.TransformerException; 027 028import org.apache.cocoon.xml.AttributesImpl; 029import org.apache.cocoon.xml.XMLUtils; 030import org.w3c.dom.Element; 031import org.xml.sax.ContentHandler; 032import org.xml.sax.SAXException; 033 034import org.ametys.core.model.type.AbstractDateElementType; 035import org.ametys.core.util.DateUtils; 036import org.ametys.plugins.repository.RepositoryConstants; 037import org.ametys.plugins.repository.data.UnknownDataException; 038import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 039import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 040import org.ametys.plugins.repository.data.type.RepositoryElementType; 041import org.ametys.runtime.model.ViewItem; 042import org.ametys.runtime.model.exception.BadItemTypeException; 043import org.ametys.runtime.model.type.DataContext; 044 045/** 046 * Class for date type of elements stored in the repository 047 */ 048public class DateRepositoryElementType extends AbstractDateElementType implements RepositoryElementType<LocalDate> 049{ 050 public Object read(RepositoryData parentData, String name) throws BadItemTypeException 051 { 052 if (!parentData.hasValue(name)) 053 { 054 return null; 055 } 056 057 if (!isCompatible(parentData, name)) 058 { 059 throw new BadItemTypeException("Try to get date value from the non date data '" + name + "' on '" + parentData + "'"); 060 } 061 062 if (parentData.isMultiple(name)) 063 { 064 return Arrays.stream(parentData.getDates(name)) 065 .map(DateUtils::asLocalDate) 066 .toArray(LocalDate[]::new); 067 } 068 else 069 { 070 Calendar date = parentData.getDate(name); 071 return DateUtils.asLocalDate(date); 072 } 073 } 074 075 @Override 076 public boolean hasValue(RepositoryData parentData, String name) throws BadItemTypeException 077 { 078 return RepositoryElementType.super.hasValue(parentData, name) || parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 079 } 080 081 public boolean hasNonEmptyValue(RepositoryData parentData, String name) throws BadItemTypeException 082 { 083 if (!parentData.hasValue(name)) 084 { 085 return false; 086 } 087 088 if (!isCompatible(parentData, name)) 089 { 090 throw new BadItemTypeException("Try to check date value from the non date data '" + name + "' on '" + parentData + "'"); 091 } 092 093 if (parentData.isMultiple(name)) 094 { 095 return parentData.getDates(name).length > 0; 096 } 097 else 098 { 099 return true; 100 } 101 } 102 103 public void write(ModifiableRepositoryData parentData, String name, Object value) throws BadItemTypeException 104 { 105 if (value == null) 106 { 107 if (parentData.hasValue(name) && parentData.isMultiple(name)) 108 { 109 parentData.setValues(name, new Calendar[0]); 110 } 111 else 112 { 113 parentData.setValue(name + EMPTY_METADATA_SUFFIX, true, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 114 115 if (parentData.hasValue(name)) 116 { 117 parentData.removeValue(name); 118 } 119 } 120 } 121 else if (value instanceof LocalDate) 122 { 123 Calendar date = DateUtils.asCalendar((LocalDate) value); 124 parentData.setValue(name, date); 125 126 if (parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)) 127 { 128 parentData.removeValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 129 } 130 } 131 else if (value instanceof LocalDate[]) 132 { 133 Calendar[] calendars = Arrays.stream((LocalDate[]) value) 134 .map(v -> Optional.ofNullable(v) 135 .orElseThrow(() -> new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"))) 136 .map(localDate -> DateUtils.asCalendar(localDate)) 137 .toArray(Calendar[]::new); 138 139 parentData.setValues(name, calendars); 140 } 141 else 142 { 143 throw new BadItemTypeException("Try to set the non date value '" + value + "' to the date data '" + name + "' on '" + parentData + "'"); 144 } 145 } 146 147 @Override 148 public void remove(ModifiableRepositoryData parentData, String name) throws UnknownDataException 149 { 150 if (parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)) 151 { 152 parentData.removeValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 153 154 if (parentData.hasValue(name)) 155 { 156 // If there is an empty metadata AND a value, delete this value. Should never happen 157 parentData.removeValue(name); 158 } 159 } 160 else 161 { 162 RepositoryElementType.super.remove(parentData, name); 163 } 164 } 165 166 public boolean isCompatible(RepositoryData parentData, String name) throws UnknownDataException 167 { 168 if (parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)) 169 { 170 return true; 171 } 172 173 if (!parentData.hasValue(name)) 174 { 175 throw new UnknownDataException("No data found for '" + name + "' in repository data named '" + parentData.getName() + "'."); 176 } 177 178 if (getRepositoryDataType().equals(parentData.getType(name))) 179 { 180 if (parentData.isMultiple(name)) 181 { 182 Calendar[] calendars = parentData.getDates(name); 183 for (Calendar calendar : calendars) 184 { 185 if (!_isDate(calendar)) 186 { 187 // If there is at least one datetime, it could not be a data of type date 188 return false; 189 } 190 } 191 192 // No date is a datetime, so it could be a data of type date 193 return true; 194 } 195 else 196 { 197 return _isDate(parentData.getDate(name)); 198 } 199 } 200 else 201 { 202 return false; 203 } 204 } 205 206 private boolean _isDate(Calendar calendar) 207 { 208 return calendar.get(Calendar.HOUR_OF_DAY) == 0 209 && calendar.get(Calendar.MINUTE) == 0 210 && calendar.get(Calendar.SECOND) == 0 211 && calendar.get(Calendar.MILLISECOND) == 0; 212 } 213 214 public String getRepositoryDataType() 215 { 216 return RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE; 217 } 218 219 @Override 220 protected LocalDate _singleValueFromXML(Element element, Optional<Object> additionalData) throws TransformerException, IOException 221 { 222 String value = element.getTextContent(); 223 ZonedDateTime zdt = DateUtils.parseZonedDateTime(value); 224 return zdt != null ? zdt.toLocalDate() : null; 225 } 226 227 @Override 228 protected void _singleValueToSAX(ContentHandler contentHandler, String tagName, LocalDate value, Optional<ViewItem> viewItem, DataContext context, AttributesImpl attributes) throws SAXException, IOException 229 { 230 AttributesImpl localAttributes = new AttributesImpl(attributes); 231 localAttributes.addCDATAAttribute("date-value", toString(value)); 232 233 ZonedDateTime zdt = value.atStartOfDay(ZoneId.systemDefault()); 234 235 // CMS-10434 : Use zoned date times and DateUtils parser for legacy compliance with SAX events generated by the old attributes API 236 String valueAsString = DateUtils.zonedDateTimeToString(zdt); 237 XMLUtils.createElement(contentHandler, tagName, localAttributes, valueAsString); 238 } 239}