001/*
002 *  Copyright 2022 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.plugins.forms.processing;
017
018import java.io.File;
019import java.time.LocalDate;
020import java.time.ZoneId;
021import java.time.ZoneOffset;
022import java.time.ZonedDateTime;
023import java.time.chrono.IsoChronology;
024import java.time.format.DateTimeFormatter;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.generation.ServiceableGenerator;
032import org.apache.cocoon.xml.AttributesImpl;
033import org.apache.cocoon.xml.XMLUtils;
034import org.apache.commons.lang.StringUtils;
035import org.xml.sax.SAXException;
036
037import org.ametys.core.util.DateUtils;
038import org.ametys.plugins.forms.Field;
039import org.ametys.plugins.forms.data.FieldValue;
040import org.ametys.plugins.forms.jcr.FormPropertiesManager;
041
042/**
043 * Abstract generator to sax form entry values
044 *
045 */
046public abstract class AbstractFormFieldGenerator extends ServiceableGenerator
047{
048    /** The form properties manager */
049    protected FormPropertiesManager _formPropManager;
050    
051    @Override
052    public void service(ServiceManager smanager) throws ServiceException
053    {
054        super.service(smanager);
055        _formPropManager = (FormPropertiesManager) smanager.lookup(FormPropertiesManager.ROLE);
056    }
057    
058    /**
059     * Sax a field value
060     * @param fdValue the field value
061     * @throws SAXException if an error occurred while saxing
062     */
063    protected void saxFieldValue(FieldValue fdValue) throws SAXException
064    {
065        saxFieldValue("field", fdValue);
066    }
067    
068    /**
069     * Sax a field value
070     * @param tagName the root tag name
071     * @param fdValue the field value
072     * @throws SAXException if an error occurred while saxing
073     */
074    protected void saxFieldValue(String tagName, FieldValue fdValue) throws SAXException
075    {
076        Field field = fdValue.getField();
077        Object rawValue = fdValue.getValue();
078        
079        String value = _getReadableValue(field, rawValue);
080        
081        if (value != null)
082        {
083            AttributesImpl atts = new AttributesImpl();
084            atts.addCDATAAttribute("label", field.getLabel());
085            atts.addCDATAAttribute("name", field.getName());
086            atts.addCDATAAttribute("columnName", fdValue.getColumnName());
087            atts.addCDATAAttribute("type", field.getType().name());
088            
089            Map<String, String> properties = field.getProperties();
090            if (properties.containsKey("regexptype"))
091            {
092                atts.addCDATAAttribute("regexptype", properties.get("regexptype"));
093            }
094            
095            XMLUtils.createElement(contentHandler, tagName, atts, value);
096        }
097    }
098    
099    /**
100     * Get the readable value as String
101     * @param field the field
102     * @param rawValue the raw value. Cannot be null.
103     * @return the value as String
104     */
105    protected String _getReadableValue(Field field, Object rawValue)
106    {
107        if (rawValue == null)
108        {
109            return null;
110        }
111        
112        switch (field.getType())
113        {
114            case SELECT:
115                boolean multiple = "true".equals(field.getProperties().get("multiple"));
116                if (multiple)
117                {
118                    String[] rawValues = ((String) rawValue).split("\n");
119                    List<String> readableValues = new ArrayList<>();
120                    for (String v : rawValues)
121                    {
122                        readableValues.add(_formPropManager.getDisplayValue(field, v));
123                    }
124                    return StringUtils.join(readableValues, ", ");
125                }
126                else
127                {
128                    return _formPropManager.getDisplayValue(field, (String) rawValue);
129                }
130            case RADIO:
131                return _formPropManager.getDisplayValue(field, (String) rawValue);
132            case TEXT:
133                String typedValue = StringUtils.trim((String) rawValue);
134                String regexptype = field.getProperties().get("regexptype");
135                if ("datetime".equals(regexptype) && StringUtils.isNotEmpty(typedValue))
136                {
137                    ZonedDateTime date = _getZonedDateTime(typedValue);
138                    if (date == null)
139                    {
140                        LocalDate localDate = _getLocalDate(typedValue);
141                        return localDate != null ? DateUtils.zonedDateTimeToString(localDate.atStartOfDay(ZoneOffset.UTC)) : null;
142                    }
143                    return DateUtils.zonedDateTimeToString(date); 
144                }
145                else if ("date".equals(regexptype) && StringUtils.isNotEmpty(typedValue))
146                {
147                    LocalDate localDate = _getLocalDate(typedValue);
148                    if (localDate == null)
149                    {
150                        ZonedDateTime date = _getZonedDateTime(typedValue);
151                        return date != null ? DateUtils.localDateToString(date.toLocalDate()) : null;
152                    }
153                    return DateUtils.localDateToString(localDate);
154                }
155                else
156                {
157                    // Trim value since some rendering (including default rendering) add many leading spaces
158                    return typedValue;
159                }
160            case FILE:
161                if (rawValue instanceof File)
162                {
163                    File file = (File) rawValue;
164                    return file.getName();
165                }
166                else
167                {
168                    return String.valueOf(rawValue);
169                }
170            case PASSWORD:
171            case CAPTCHA:
172                // ignore value
173                return null;
174            default:
175                return String.valueOf(rawValue);
176        }
177    }
178    
179    /**
180     * Get the zoned date time from string value
181     * @param value the string value
182     * @return the zoned date time. Null if string value can't be parsed
183     */
184    protected ZonedDateTime _getZonedDateTime(String value)
185    {
186        try
187        {
188            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm")
189                    .withZone(ZoneId.systemDefault())
190                    .withChronology(IsoChronology.INSTANCE);
191            
192            return ZonedDateTime.parse(value, formatter);
193        }
194        catch (Exception e) 
195        {
196            return null;
197        }
198    }
199    
200    /**
201     * Get the local date from string value
202     * @param value the string value
203     * @return the local date time. Null if string value can't be parsed
204     */
205    protected LocalDate _getLocalDate(String value)
206    {
207        try
208        {
209            return DateUtils.parseLocalDate(value);
210        }
211        catch (Exception e) 
212        {
213            return null;
214        }
215    }
216}