001/* 002 * Copyright 2019 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.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Optional; 022import java.util.stream.Stream; 023 024import org.apache.cocoon.xml.AttributesImpl; 025import org.apache.cocoon.xml.XMLUtils; 026import org.apache.commons.lang3.StringUtils; 027import org.apache.commons.lang3.tuple.Triple; 028import org.w3c.dom.Element; 029import org.xml.sax.ContentHandler; 030import org.xml.sax.SAXException; 031 032import org.ametys.cms.data.Reference; 033import org.ametys.core.model.type.AbstractElementType; 034import org.ametys.core.model.type.ModelItemTypeHelper; 035import org.ametys.runtime.model.ViewItem; 036import org.ametys.runtime.model.compare.DataChangeType; 037import org.ametys.runtime.model.compare.DataChangeTypeDetail; 038import org.ametys.runtime.model.exception.BadItemTypeException; 039import org.ametys.runtime.model.type.DataContext; 040 041/** 042 * Abstract class for reference type of elements 043 */ 044public abstract class AbstractReferenceElementType extends AbstractElementType<Reference> 045{ 046 /** The separator between the type and the value for the string representation of a reference */ 047 protected static final String __SEPARATOR = "#"; 048 049 @Override 050 public Reference convertValue(Object value) throws BadItemTypeException 051 { 052 if (value instanceof String) 053 { 054 String strValue = (String) value; 055 if (StringUtils.isEmpty(strValue)) 056 { 057 return null; 058 } 059 060 if (strValue.startsWith(__SEPARATOR)) 061 { 062 strValue = strValue.substring(1); 063 String referenceValue = StringUtils.substringAfter(strValue, __SEPARATOR); 064 String referenceType = StringUtils.substringBefore(strValue, __SEPARATOR); 065 return new Reference(referenceValue, referenceType); 066 } 067 else 068 { 069 return new Reference(strValue); 070 } 071 } 072 073 return super.convertValue(value); 074 } 075 076 @Override 077 public String toString(Reference value) 078 { 079 if (value != null) 080 { 081 // If the type is the reference default type, do not export it 082 if (value.getType().equals(Reference.DEFAULT_TYPE)) 083 { 084 return value.getValue(); 085 } 086 087 return __SEPARATOR + value.getType() + __SEPARATOR + value.getValue(); 088 } 089 else 090 { 091 return null; 092 } 093 } 094 095 @SuppressWarnings("unchecked") 096 public Object fromJSONForClient(Object json, DataContext context) 097 { 098 if (json == null) 099 { 100 return json; 101 } 102 else if (json instanceof String) 103 { 104 return convertValue(json); 105 } 106 else if (json instanceof Map) 107 { 108 return _json2Reference((Map<String, Object>) json); 109 } 110 else if (json instanceof List) 111 { 112 List<Object> jsonList = (List<Object>) json; 113 Reference[] result = new Reference[jsonList.size()]; 114 for (int i = 0; i < jsonList.size(); i++) 115 { 116 Object singleJSON = jsonList.get(i); 117 if (singleJSON instanceof Map) 118 { 119 result[i] = _json2Reference((Map<String, Object>) singleJSON); 120 } 121 else if (singleJSON instanceof String) 122 { 123 result[i] = convertValue(singleJSON); 124 } 125 else 126 { 127 throw new IllegalArgumentException("Try to convert the non reference JSON object '" + json + "' into a reference"); 128 } 129 } 130 return result; 131 } 132 else 133 { 134 throw new IllegalArgumentException("Try to convert the non reference JSON object '" + json + "' into a reference"); 135 } 136 } 137 138 private Reference _json2Reference(Map<String, Object> json) 139 { 140 // If the reference is empty, return null 141 return Optional.ofNullable(json) 142 .filter(reference -> !reference.isEmpty() 143 && reference.get(Reference.VALUE_IDENTIFIER) != null && reference.get(Reference.VALUE_IDENTIFIER) instanceof String 144 && reference.get(Reference.TYPE_IDENTIFIER) == null || reference.get(Reference.TYPE_IDENTIFIER) instanceof String) 145 .map(reference -> new Reference((String) reference.get(Reference.VALUE_IDENTIFIER), (String) reference.get(Reference.TYPE_IDENTIFIER))) 146 .orElse(null); 147 } 148 149 @Override 150 protected Object _singleValueToJSON(Reference value, DataContext context) 151 { 152 Map<String, Object> referenceInfos = new HashMap<>(); 153 154 referenceInfos.put(Reference.VALUE_IDENTIFIER, value.getValue()); 155 referenceInfos.put(Reference.TYPE_IDENTIFIER, value.getType()); 156 157 return referenceInfos; 158 } 159 160 @Override 161 protected Reference _singleValueFromXML(Element element, Optional<Object> additionalData) 162 { 163 String type = element.getAttribute(Reference.TYPE_IDENTIFIER); 164 String value = element.getTextContent(); 165 return new Reference(value, type); 166 } 167 168 @Override 169 protected void _singleValueToSAX(ContentHandler contentHandler, String tagName, Reference value, Optional<ViewItem> viewItem, DataContext context, AttributesImpl attributes) throws SAXException 170 { 171 AttributesImpl localAttributes = new AttributesImpl(attributes); 172 localAttributes.addCDATAAttribute("type", value.getType()); 173 XMLUtils.createElement(contentHandler, tagName, localAttributes, value.getValue()); 174 } 175 176 @Override 177 protected Stream<Triple<DataChangeType, DataChangeTypeDetail, String>> _compareMultipleValues(Reference[] value1, Reference[] value2) 178 { 179 throw new UnsupportedOperationException("Unable to compare multiple values of type '" + getId() + "'"); 180 } 181 182 @Override 183 protected Stream<Triple<DataChangeType, DataChangeTypeDetail, String>> _compareSingleValues(Reference value1, Reference value2) 184 { 185 if (ModelItemTypeHelper.areSingleObjectsBothNotNullAndDifferents(value1, value2)) 186 { 187 return Stream.of 188 ( 189 ModelItemTypeHelper.compareSingleObjects(value1.getType(), value2.getType(), Reference.TYPE_IDENTIFIER), 190 ModelItemTypeHelper.compareSingleObjects(value1.getValue(), value2.getValue(), Reference.VALUE_IDENTIFIER) 191 ) 192 .filter(Optional::isPresent) 193 .map(Optional::get); 194 } 195 else 196 { 197 return Stream.of(ModelItemTypeHelper.compareSingleObjects(value1, value2, StringUtils.EMPTY)) 198 .filter(Optional::isPresent) 199 .map(Optional::get); 200 } 201 } 202 203 public boolean isSimple() 204 { 205 return false; 206 } 207 208 @Override 209 protected boolean _useJSONForEdition() 210 { 211 return true; 212 } 213}