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.ArrayList;
019import java.util.Arrays;
020import java.util.List;
021import java.util.Locale;
022import java.util.stream.Collectors;
023
024import org.apache.avalon.framework.component.Component;
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.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.commons.lang3.StringUtils;
032import org.xml.sax.ContentHandler;
033import org.xml.sax.SAXException;
034
035import org.ametys.cms.data.Binary;
036import org.ametys.cms.data.ContentValue;
037import org.ametys.cms.data.ExplorerFile;
038import org.ametys.cms.data.File;
039import org.ametys.core.model.type.AbstractElementType;
040import org.ametys.plugins.repository.AmetysObjectResolver;
041import org.ametys.runtime.model.exception.BadItemTypeException;
042
043/**
044 * Abstract class for file type of elements
045 */
046public abstract class AbstractFileElementType extends AbstractElementType<File> implements Component, Serviceable, Configurable
047{
048    /** Type of he file from explorer */
049    protected static final String __EXPLORER_FILE_TYPE = "explorer";
050    
051    /** Ametys object resolver */
052    protected AmetysObjectResolver _resolver;
053    
054    /** Type of the binary */
055    protected String _binaryType;
056    
057    public void service(ServiceManager manager) throws ServiceException
058    {
059        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
060    }
061    
062    public void configure(Configuration configuration) throws ConfigurationException
063    {
064        Configuration typeNode = configuration.getChild("binary-type", false);
065        if (typeNode != null)
066        {
067            _binaryType = typeNode.getValue(null);
068        }
069        
070        if (StringUtils.isBlank(_binaryType))
071        {
072            throw new ConfigurationException("The configuration of binary type should contain a node 'binary-type' with the type of the binary ('metadata', 'explorer', ...)", configuration);
073        }
074    }
075    
076    public File castValue(String value) throws BadItemTypeException
077    {
078        return new ExplorerFile(_resolver, value);
079    }
080
081    @Override
082    public String toString(File value)
083    {
084        if (value instanceof ExplorerFile)
085        {
086            return ((ExplorerFile) value).getResourceId();
087        }
088        else
089        {
090            throw new UnsupportedOperationException("Unable to convert a binary to a string value");
091        }
092    }
093    
094    @Override
095    public void valueToSAX(ContentHandler contentHandler, String tagName, Object value, Locale locale) throws SAXException
096    {
097        if (value != null)
098        {
099            if (value instanceof String)
100            {
101                ExplorerFile file = new ExplorerFile(_resolver, (String) value);
102                ResourceElementTypeHelper.singleFileToSAX(contentHandler, tagName, file, __EXPLORER_FILE_TYPE);
103            }
104            else if (value instanceof String[])
105            {
106                for (String fileId : (String[]) value)
107                {
108                    ExplorerFile file = new ExplorerFile(_resolver, fileId);
109                    ResourceElementTypeHelper.singleFileToSAX(contentHandler, tagName, file, __EXPLORER_FILE_TYPE);
110                }
111            }
112            else if (value instanceof ExplorerFile)
113            {
114                ResourceElementTypeHelper.singleFileToSAX(contentHandler, tagName, (ExplorerFile) value, __EXPLORER_FILE_TYPE);
115            }
116            else if (value instanceof ExplorerFile[])
117            {
118                for (ExplorerFile file : (ExplorerFile[]) value)
119                {
120                    ResourceElementTypeHelper.singleFileToSAX(contentHandler, tagName, file, __EXPLORER_FILE_TYPE);
121                }
122            }
123            else if (value instanceof Binary)
124            {
125                ResourceElementTypeHelper.singleFileToSAX(contentHandler, tagName, (Binary) value, _binaryType);
126            }
127            else if (value instanceof Binary[])
128            {
129                for (Binary binary : (Binary[]) value)
130                {
131                    ResourceElementTypeHelper.singleFileToSAX(contentHandler, tagName, binary, _binaryType);
132                }
133            }
134            else
135            {
136                throw new IllegalArgumentException("Try to sax the non file value '" + value + "' in tag name '" + tagName + "'");
137            }
138        }
139    }
140    
141    @Override
142    public File parseConfiguration(Configuration configuration) throws ConfigurationException
143    {
144        if (configuration != null)
145        {
146            String fileId = configuration.getAttribute("id", null);
147            if (StringUtils.isNotEmpty(fileId))
148            {
149                return new ExplorerFile(_resolver, fileId);
150            }
151        }
152        
153        return null;
154    }
155    
156    @Override
157    public Object valueToJSONForClient(Object value)
158    {
159        if (value == null)
160        {
161            return value;
162        }
163        else if (value instanceof String)
164        {
165            ExplorerFile file = new ExplorerFile(_resolver, (String) value);
166            return ResourceElementTypeHelper.singleFileToJSON(file, __EXPLORER_FILE_TYPE);
167        }
168        else if (value instanceof String[])
169        {
170            return Arrays.asList((String[]) value).stream()
171                                                  .map(fileId -> new ExplorerFile(_resolver, fileId))
172                                                  .map(file -> ResourceElementTypeHelper.singleFileToJSON(file, __EXPLORER_FILE_TYPE))
173                                                  .collect(Collectors.toList());
174        }
175        else if (value instanceof ExplorerFile)
176        {
177            return ResourceElementTypeHelper.singleFileToJSON((ExplorerFile) value, __EXPLORER_FILE_TYPE);
178        }
179        else if (value instanceof ExplorerFile[])
180        {
181            return Arrays.asList((ExplorerFile[]) value).stream()
182                                                        .map(file -> ResourceElementTypeHelper.singleFileToJSON(file, __EXPLORER_FILE_TYPE))
183                                                        .collect(Collectors.toList());
184        }
185        else if (value instanceof Binary)
186        {
187            return ResourceElementTypeHelper.singleFileToJSON((Binary) value, _binaryType);
188        }
189        else if (value instanceof Binary[])
190        {
191            return Arrays.asList((Binary[]) value).stream()
192                                                  .map(file -> ResourceElementTypeHelper.singleFileToJSON(file, _binaryType))
193                                                  .collect(Collectors.toList());
194        }
195        else
196        {
197            throw new IllegalArgumentException("Try to convert the non file value '" + value + "' to JSON");
198        }
199    }
200    
201    public Object fromJSONForClient(Object json)
202    {
203        if (json == null)
204        {
205            return json;
206        }
207        else if (json instanceof String)
208        {
209            return castValue((String) json);
210        }
211        // TODO NEWATTRIBUTEAPI_CONTENT: This implementation only manages json object as string containing explorer file id. Fix this implementation when needed to migrate the EditContentFunction 
212        else if (json instanceof List)
213        {
214            @SuppressWarnings("unchecked")
215            List<String> jsonList = (List<String>) json;
216            List<File> fileList = new ArrayList<>();
217            for (String singleJSON : jsonList)
218            {
219                fileList.add(castValue(singleJSON));
220            }
221            return fileList.toArray(new ContentValue[fileList.size()]);
222        }
223        else
224        {
225            throw new IllegalArgumentException("Try to convert the non file time JSON object '" + json + "' into a file");
226        }
227    }
228
229    public boolean isSimple()
230    {
231        return false;
232    }
233    
234    /**
235     * Check if ametys objects exist with the given identifiers
236     * @param identifiers the identifiers
237     * @return <code>true</code> if ametys object exist for each identifier, <code>false</code> otherwise
238     */
239    protected boolean _hasAmetysObjectsForIdentifiers(String... identifiers)
240    {
241        for (String identifier : identifiers)
242        {
243            // If there is at list one identifier that does not match an ametys object => return false
244            if (identifier.indexOf("://") < 0 || !_resolver.hasAmetysObjectForId(identifier))
245            {
246                return false;
247            }
248        }
249        
250        // All identifiers match an ametys object
251        return true;
252    }
253}