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