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.time.ZonedDateTime; 019import java.time.format.DateTimeFormatter; 020import java.util.Arrays; 021import java.util.Calendar; 022import java.util.Date; 023import java.util.Optional; 024import java.util.TimeZone; 025 026import org.apache.cocoon.xml.AttributesImpl; 027import org.apache.cocoon.xml.XMLUtils; 028import org.w3c.dom.Element; 029import org.xml.sax.ContentHandler; 030import org.xml.sax.SAXException; 031 032import org.ametys.core.model.type.AbstractDateTimeElementType; 033import org.ametys.core.util.DateUtils; 034import org.ametys.plugins.repository.RepositoryConstants; 035import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 036import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 037import org.ametys.plugins.repository.data.type.RepositoryElementType; 038import org.ametys.runtime.model.ViewItem; 039import org.ametys.runtime.model.exception.BadItemTypeException; 040import org.ametys.runtime.model.type.DataContext; 041 042/** 043 * Class for date time type of elements stored in the repository 044 */ 045public class DateTimeRepositoryElementType extends AbstractDateTimeElementType implements RepositoryElementType<ZonedDateTime> 046{ 047 /** 048 * {@inheritDoc} 049 * The DateTime type retrieves a single or an array of {@link ZonedDateTime}. 050 * To convert a {@link ZonedDateTime} in {@link Date} and keep the {@link TimeZone} information, use the {@link ZonedDateTime#toEpochSecond()} method. 051 */ 052 public Object read(RepositoryData parentData, String name) throws BadItemTypeException 053 { 054 if (!parentData.hasValue(name)) 055 { 056 return null; 057 } 058 059 if (!isCompatible(parentData, name)) 060 { 061 throw new BadItemTypeException("Try to get date time value from the non date time data '" + name + "' on '" + parentData + "'"); 062 } 063 064 if (parentData.isMultiple(name)) 065 { 066 return Arrays.stream(parentData.getDates(name)) 067 .map(DateUtils::asZonedDateTime) 068 .toArray(ZonedDateTime[]::new); 069 } 070 else 071 { 072 Calendar date = parentData.getDate(name); 073 return DateUtils.asZonedDateTime(date); 074 } 075 } 076 077 @Override 078 public boolean hasValue(RepositoryData parentData, String name) throws BadItemTypeException 079 { 080 return RepositoryElementType.super.hasValue(parentData, name) || parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 081 } 082 083 public boolean hasNonEmptyValue(RepositoryData parentData, String name) throws BadItemTypeException 084 { 085 if (!parentData.hasValue(name)) 086 { 087 return false; 088 } 089 090 if (!isCompatible(parentData, name)) 091 { 092 throw new BadItemTypeException("Try to check date time value from the non date time data '" + name + "' on '" + parentData + "'"); 093 } 094 095 if (parentData.isMultiple(name)) 096 { 097 return parentData.getDates(name).length > 0; 098 } 099 else 100 { 101 return true; 102 } 103 } 104 105 public void write(ModifiableRepositoryData parentData, String name, Object value) throws BadItemTypeException 106 { 107 if (value == null) 108 { 109 if (parentData.hasValue(name) && parentData.isMultiple(name)) 110 { 111 parentData.setValues(name, new Calendar[0]); 112 } 113 else 114 { 115 parentData.setValue(name + EMPTY_METADATA_SUFFIX, true, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 116 117 if (parentData.hasValue(name)) 118 { 119 parentData.removeValue(name); 120 } 121 } 122 } 123 else if (value instanceof ZonedDateTime) 124 { 125 Calendar date = DateUtils.asCalendar((ZonedDateTime) value); 126 parentData.setValue(name, date); 127 128 if (parentData.hasValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL)) 129 { 130 parentData.removeValue(name + EMPTY_METADATA_SUFFIX, RepositoryConstants.NAMESPACE_PREFIX_INTERNAL); 131 } 132 } 133 else if (value instanceof ZonedDateTime[]) 134 { 135 Calendar[] calendars = Arrays.stream((ZonedDateTime[]) value) 136 .map(v -> Optional.ofNullable(v) 137 .orElseThrow(() -> new IllegalArgumentException("Try to set a null value into the multiple " + getId() + " data '" + name + "' on '" + parentData + "'"))) 138 .map(zonedDateTime -> DateUtils.asCalendar(zonedDateTime)) 139 .toArray(Calendar[]::new); 140 141 parentData.setValues(name, calendars); 142 } 143 else 144 { 145 throw new BadItemTypeException("Try to set the non date time value '" + value + "' to the date time data '" + name + "' on '" + parentData + "'"); 146 } 147 } 148 149 public String getRepositoryDataType() 150 { 151 return RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE; 152 } 153 154 @Override 155 protected ZonedDateTime _singleValueFromXML(Element element, Optional<Object> additionalData) 156 { 157 String value = element.getTextContent(); 158 Optional<DateTimeFormatter> formatter = additionalData.filter(DateTimeFormatter.class::isInstance) 159 .map(DateTimeFormatter.class::cast); 160 161 return DateUtils.parseZonedDateTime(value, formatter); 162 } 163 164 @Override 165 protected void _singleValueToSAX(ContentHandler contentHandler, String tagName, ZonedDateTime value, Optional<ViewItem> viewItem, DataContext context, AttributesImpl attributes) throws SAXException 166 { 167 String valueAsString = DateUtils.zonedDateTimeToString(value); 168 XMLUtils.createElement(contentHandler, tagName, attributes, valueAsString); 169 } 170}