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