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.impl;
017
018import java.io.IOException;
019import java.util.Collection;
020import java.util.Optional;
021
022import org.apache.cocoon.xml.AttributesImpl;
023import org.apache.cocoon.xml.XMLUtils;
024import org.apache.commons.lang3.math.NumberUtils;
025import org.xml.sax.Attributes;
026import org.xml.sax.ContentHandler;
027import org.xml.sax.SAXException;
028
029import org.ametys.core.model.type.AbstractModelItemType;
030import org.ametys.plugins.repository.RepositoryConstants;
031import org.ametys.plugins.repository.data.UnknownDataException;
032import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeater;
033import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeaterEntry;
034import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper;
035import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
036import org.ametys.plugins.repository.data.type.RepositoryModelItemGroupType;
037import org.ametys.plugins.repository.model.RepeaterDefinition;
038import org.ametys.runtime.model.ModelItemContainer;
039import org.ametys.runtime.model.ModelViewItemGroup;
040import org.ametys.runtime.model.ViewItem;
041import org.ametys.runtime.model.ViewItemAccessor;
042import org.ametys.runtime.model.type.DataContext;
043
044/**
045 * Class for repeater type
046 */
047public class RepeaterRepositoryModelItemType extends AbstractModelItemType implements RepositoryModelItemGroupType
048{
049    public void valueToSAXForEdition(ContentHandler contentHandler, String tagName, Object value, Optional<ViewItem> viewItem, DataContext context) throws SAXException, IOException
050    {
051        _valueToSAX(contentHandler, tagName, value, viewItem, context, true);
052    }
053    
054    public void valueToSAX(ContentHandler contentHandler, String tagName, Object value, Optional<ViewItem> viewItem, DataContext context) throws SAXException, IOException
055    {
056        _valueToSAX(contentHandler, tagName, value, viewItem, context, false);
057    }
058    
059    private void _valueToSAX(ContentHandler contentHandler, String tagName, Object value, Optional<ViewItem> viewItem, DataContext context, boolean isEdition) throws SAXException, IOException
060    {
061        AttributesImpl attributes = _getContextAttributes(context, true);
062        
063        if (value == null)
064        {
065            XMLUtils.createElement(contentHandler, tagName, attributes);
066        }
067        else if (value instanceof ModelAwareRepeater)
068        {
069            ModelAwareRepeater repeater = (ModelAwareRepeater) value;
070            
071            attributes.addCDATAAttribute("entryCount", String.valueOf(repeater.getSize()));
072            
073            ViewItemAccessor viewItemAccessor = viewItem.filter(ViewItemAccessor.class::isInstance)
074                    .map(ViewItemAccessor.class::cast)
075                    .orElse(ModelViewItemGroup.of(repeater.getModel()));
076
077            XMLUtils.startElement(contentHandler, tagName, attributes);
078            for (ModelAwareRepeaterEntry entry : repeater.getEntries())
079            {
080                _repeaterEntryToSAX(contentHandler, entry, viewItemAccessor, context, isEdition);
081            }
082            XMLUtils.endElement(contentHandler, tagName);
083        }
084        else if (value instanceof ModelAwareRepeaterEntry)
085        {
086            ModelAwareRepeaterEntry entry = (ModelAwareRepeaterEntry) value;
087            
088            ViewItemAccessor viewItemAccessor = viewItem.filter(ViewItemAccessor.class::isInstance)
089                    .map(ViewItemAccessor.class::cast)
090                    .orElse(ModelViewItemGroup.of(_getRepeaterEntryDefinition(entry)));
091            
092            _repeaterEntryToSAX(contentHandler, entry, viewItemAccessor, context, isEdition);
093        }
094        else
095        {
096            throw new IllegalArgumentException("Try to sax the non repeater value '" + value + "' in tag name '" + tagName + "'");
097        }
098    }
099
100    private void _repeaterEntryToSAX(ContentHandler contentHandler, ModelAwareRepeaterEntry entry, ViewItemAccessor viewItemAccessor, DataContext context, boolean isEdition) throws SAXException, IOException
101    {
102        DataContext newContext = context.cloneContext().addSuffixToLastSegment("[" + entry.getPosition() + "]");
103        
104        XMLUtils.startElement(contentHandler, "entry", _getEntryAttributes(entry, newContext));
105        DataHolderHelper.dataToSAX(entry, contentHandler, viewItemAccessor, newContext, isEdition);
106        XMLUtils.endElement(contentHandler, "entry");
107    }
108    
109    private Attributes _getEntryAttributes(ModelAwareRepeaterEntry entry, DataContext context)
110    {
111        AttributesImpl entryAttrs = _getContextAttributes(context, false);
112        String entryName = Integer.toString(entry.getPosition());
113        entryAttrs.addCDATAAttribute("name", entryName);
114        return entryAttrs;
115    }
116    
117    private RepeaterDefinition _getRepeaterEntryDefinition(ModelAwareRepeaterEntry entry)
118    {
119        Collection<? extends ModelItemContainer> modelItemContainers = entry.getModel();
120        assert modelItemContainers.size() == 1;
121        
122        ModelItemContainer modelItemContainer = modelItemContainers.iterator().next();
123        assert modelItemContainer instanceof RepeaterDefinition;
124        
125        return (RepeaterDefinition) modelItemContainer;
126    }
127    
128    public boolean isCompatible(RepositoryData parentData, String name) throws UnknownDataException
129    {
130        if (RepositoryModelItemGroupType.super.isCompatible(parentData, name))
131        {
132            return true;
133        }
134        // TODO NEWATTRIBUTEAPI: This part of the test is here just to be compatible with the old compositMetadata type. When all repeaters are migrated, remove this part of the test
135        else if (RepositoryConstants.COMPOSITE_METADTA_NODETYPE.equals(parentData.getType(name)))
136        {
137            RepositoryData possibleRepeater = parentData.getRepositoryData(name);
138            for (String dataName : possibleRepeater.getDataNames())
139            {
140                if (!NumberUtils.isParsable(dataName))
141                {
142                    // If there is at least one data that is not a number (entry position), this data can not be a repeater
143                    return false;
144                }
145            }
146            
147            // All data are numbers and could be entry positions, the data could be a repeater
148            return true;
149        }
150        else
151        {
152            return false;
153        }
154    }
155    
156    public String getRepositoryDataType()
157    {
158        return RepositoryConstants.REPEATER_NODETYPE;
159    }
160}