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;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.Map;
022import java.util.Optional;
023import java.util.function.Function;
024import java.util.stream.Stream;
025
026import javax.xml.transform.TransformerException;
027
028import org.apache.cocoon.xml.AttributesImpl;
029import org.apache.commons.lang3.StringUtils;
030import org.apache.commons.lang3.tuple.ImmutableTriple;
031import org.apache.commons.lang3.tuple.Triple;
032import org.w3c.dom.Element;
033import org.xml.sax.ContentHandler;
034import org.xml.sax.SAXException;
035
036import org.ametys.core.model.type.AbstractElementType;
037import org.ametys.core.model.type.ModelItemTypeHelper;
038import org.ametys.plugins.repository.metadata.MultilingualString;
039import org.ametys.plugins.repository.metadata.MultilingualStringHelper;
040import org.ametys.runtime.model.ViewItem;
041import org.ametys.runtime.model.compare.DataChangeType;
042import org.ametys.runtime.model.compare.DataChangeTypeDetail;
043import org.ametys.runtime.model.exception.BadItemTypeException;
044import org.ametys.runtime.model.type.DataContext;
045
046/**
047 * Abstract class for multilingual string type of elements
048 */
049public abstract class AbstractMultilingualStringElementType extends AbstractElementType<MultilingualString>
050{
051    @Override
052    public MultilingualString convertValue(Object value)
053    {
054        if (value instanceof String)
055        {
056            try
057            {
058                return MultilingualStringHelper.fromString((String) value);
059            }
060            catch (IllegalArgumentException e)
061            {
062                throw new BadItemTypeException("Unable to cast '" + value + "' to a multilingual string");
063            }
064        }
065        
066        return super.convertValue(value);
067    }
068    
069    @Override
070    public String toString(MultilingualString value)
071    {
072        return MultilingualStringHelper.toString(value);
073    }
074
075    @SuppressWarnings("unchecked")
076    public Object fromJSONForClient(Object json)
077    {
078        if (json == null)
079        {
080            return json;
081        }
082        else if (json instanceof Map)
083        {
084            return MultilingualStringHelper.fromJSON((Map<String, Object>) json);
085        }
086        else if (json instanceof List)
087        {
088            List<MultilingualString> multilingualStrings = new ArrayList<>();
089            for (Map<String, Object> singleJson : (List<Map<String, Object>>) json)
090            {
091                multilingualStrings.add(MultilingualStringHelper.fromJSON(singleJson));
092            }
093            return multilingualStrings.toArray(new MultilingualString[multilingualStrings.size()]);
094        }
095        else
096        {
097            throw new IllegalArgumentException("Try to convert the non multilingual string JSON object '" + json + "' into a multilingual string");
098        }
099    }
100    
101    @Override
102    protected Object _singleValueToJSON(MultilingualString value, DataContext context)
103    {
104        return MultilingualStringHelper.toJson(value);
105    }
106    
107    @Override
108    protected MultilingualString _singleValueFromXML(Element element, Optional<Object> additionalData) throws TransformerException, IOException
109    {
110        return MultilingualStringHelper.fromXML(element);
111    }
112    
113    @Override
114    protected void _singleValueToSAX(ContentHandler contentHandler, String tagName, MultilingualString value, Optional<ViewItem> viewItem, DataContext context, AttributesImpl attributes) throws SAXException
115    {
116        MultilingualStringHelper.sax(contentHandler, tagName, value, attributes, context.getLocale());
117    }
118    
119    @Override
120    protected Stream<Triple<DataChangeType, DataChangeTypeDetail, String>> _compareMultipleValues(MultilingualString[] value1, MultilingualString[] value2) throws IOException
121    {
122        throw new UnsupportedOperationException("Unable to compare multiple values of type '" + getId() + "'");
123    }
124    
125    @Override
126    protected Stream<Triple<DataChangeType, DataChangeTypeDetail, String>> _compareSingleValues(MultilingualString value1, MultilingualString value2) throws IOException
127    {
128        if (ModelItemTypeHelper.areSingleObjectsBothNotNullAndDifferents(value1, value2))
129        {
130            return Stream.of
131                    (
132                        // Manages modified and removed strings
133                        value1.getLocales()
134                            .stream()
135                            .map
136                            (
137                                locale -> ModelItemTypeHelper.compareSingleStrings
138                                (
139                                    value1.getValue(locale),
140                                    value2.hasLocale(locale) ? value2.getValue(locale) : StringUtils.EMPTY,
141                                    locale.toString()
142                                )
143                            )
144                            .filter(Optional::isPresent)
145                            .map(Optional::get),
146
147                        // Manages added strings
148                        value2.getLocales()
149                            .stream()
150                            .filter(locale -> !value1.hasLocale(locale))
151                            .map(locale -> new ImmutableTriple<>(DataChangeType.ADDED, DataChangeTypeDetail.NONE, locale.toString()))
152                    )
153                    .flatMap(Function.identity());
154        }
155        else
156        {
157            return Stream.of(ModelItemTypeHelper.compareSingleObjects(value1, value2, StringUtils.EMPTY))
158                         .filter(Optional::isPresent)
159                         .map(Optional::get);
160        }
161    }
162    
163    public boolean isSimple()
164    {
165        return true;
166    }
167    
168    @Override
169    protected boolean _useJSONForEdition()
170    {
171        return true;
172    }
173}