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