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.Map; 020import java.util.Optional; 021import java.util.stream.Stream; 022 023import javax.xml.transform.TransformerException; 024 025import org.apache.avalon.framework.configuration.Configurable; 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.context.ContextException; 029import org.apache.avalon.framework.context.Contextualizable; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.cocoon.Constants; 033import org.apache.cocoon.environment.Context; 034import org.apache.cocoon.xml.AttributesImpl; 035import org.apache.commons.lang3.StringUtils; 036import org.apache.commons.lang3.tuple.Triple; 037import org.w3c.dom.Element; 038import org.xml.sax.ContentHandler; 039import org.xml.sax.SAXException; 040 041import org.ametys.cms.data.Binary; 042import org.ametys.core.model.type.AbstractElementType; 043import org.ametys.core.model.type.ModelItemTypeHelper; 044import org.ametys.core.upload.Upload; 045import org.ametys.core.upload.UploadManager; 046import org.ametys.core.user.CurrentUserProvider; 047import org.ametys.plugins.repository.data.holder.values.UntouchedValue; 048import org.ametys.runtime.model.ViewItem; 049import org.ametys.runtime.model.compare.DataChangeType; 050import org.ametys.runtime.model.compare.DataChangeTypeDetail; 051import org.ametys.runtime.model.type.DataContext; 052 053/** 054 * Abstract class for binary type of elements 055 */ 056public abstract class AbstractBinaryElementType extends AbstractElementType<Binary> implements Configurable, Contextualizable 057{ 058 /** Constant for non-modified binary during edition */ 059 public static final String UNTOUCHED = "untouched"; 060 061 /** Type of the binary */ 062 protected String _binaryType; 063 064 /** {@link UploadManager} */ 065 protected UploadManager _uploadManager; 066 067 /** {@link CurrentUserProvider} */ 068 protected CurrentUserProvider _userProvider; 069 070 /** Coocon's context */ 071 protected Context _context; 072 073 public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException 074 { 075 _context = (Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 076 } 077 078 @Override 079 public void service(ServiceManager manager) throws ServiceException 080 { 081 super.service(manager); 082 _uploadManager = (UploadManager) manager.lookup(UploadManager.ROLE); 083 _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 084 } 085 086 public void configure(Configuration configuration) throws ConfigurationException 087 { 088 Configuration typeNode = configuration.getChild("type", false); 089 if (typeNode != null) 090 { 091 _binaryType = typeNode.getValue(null); 092 } 093 094 if (StringUtils.isBlank(_binaryType)) 095 { 096 throw new ConfigurationException("The configuration of binary type should contain a node 'binary-type' with the type of the binary ('metadata', 'explorer', ...)", configuration); 097 } 098 } 099 100 @Override 101 public String toString(Binary value) 102 { 103 throw new UnsupportedOperationException("Unable to convert a binary to a string value"); 104 } 105 106 public Object fromJSONForClient(Object json) 107 { 108 if (json == null) 109 { 110 return null; 111 } 112 113 if (json instanceof Map) 114 { 115 Map data = (Map) json; 116 if (data.isEmpty()) 117 { 118 return null; 119 } 120 121 String id = (String) ((Map) json).get("id"); 122 if (id.equals(UNTOUCHED)) 123 { 124 return new UntouchedValue(); 125 } 126 127 Upload upload = _uploadManager.getUpload(_userProvider.getUser(), id); 128 return ResourceElementTypeHelper.binaryFromUpload(upload); 129 } 130 131 throw new IllegalArgumentException("Try to convert the non binary JSON object '" + json + "' into a binary"); 132 } 133 134 @Override 135 public boolean isCompatible(Object value) 136 { 137 return super.isCompatible(value) || value instanceof UntouchedValue; 138 } 139 140 @Override 141 protected Object _singleValueToJSON(Binary value, DataContext context) 142 { 143 return ResourceElementTypeHelper.singleBinaryToJSON(value, _binaryType, context); 144 } 145 146 @Override 147 protected Binary _singleValueFromXML(Element element, Optional<Object> additionalData) throws TransformerException, IOException 148 { 149 Binary binary = new Binary(); 150 ResourceElementTypeHelper.resourceFromXML(binary, element, additionalData, _context); 151 return binary; 152 } 153 154 @Override 155 protected void _singleValueToSAX(ContentHandler contentHandler, String tagName, Binary binary, Optional<ViewItem> viewItem, DataContext context, AttributesImpl attributes) throws SAXException 156 { 157 ResourceElementTypeHelper.singleBinaryToSAX(contentHandler, tagName, binary, _binaryType, context, attributes); 158 } 159 160 @Override 161 protected Stream<Triple<DataChangeType, DataChangeTypeDetail, String>> _compareMultipleValues(Binary[] value1, Binary[] value2) throws IOException 162 { 163 throw new UnsupportedOperationException("Unable to compare multiple values of type '" + getId() + "'"); 164 } 165 166 @Override 167 protected Stream<Triple<DataChangeType, DataChangeTypeDetail, String>> _compareSingleValues(Binary value1, Binary value2) throws IOException 168 { 169 if (ModelItemTypeHelper.areSingleObjectsBothNotNullAndDifferents(value1, value2)) 170 { 171 return ResourceElementTypeHelper.compareSingleBinaries(value1, value2); 172 } 173 else 174 { 175 return Stream.of(ModelItemTypeHelper.compareSingleObjects(value1, value2, StringUtils.EMPTY)) 176 .filter(Optional::isPresent) 177 .map(Optional::get); 178 } 179 } 180 181 public boolean isSimple() 182 { 183 return false; 184 } 185 186 @Override 187 protected boolean _useJSONForEdition() 188 { 189 return true; 190 } 191}