001/*
002 *  Copyright 2018 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.plugins.repository.data.holder.group.impl;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.Comparator;
022import java.util.List;
023import java.util.SortedSet;
024import java.util.TreeSet;
025
026import org.apache.cocoon.xml.AttributesImpl;
027import org.apache.cocoon.xml.XMLUtils;
028import org.xml.sax.Attributes;
029import org.xml.sax.ContentHandler;
030import org.xml.sax.SAXException;
031
032import org.ametys.plugins.repository.data.holder.DataHolder;
033import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
034import org.ametys.plugins.repository.data.holder.group.ModifiableRepeater;
035import org.ametys.plugins.repository.data.holder.group.ModifiableRepeaterEntry;
036import org.ametys.plugins.repository.data.holder.group.Repeater;
037import org.ametys.plugins.repository.data.holder.group.RepeaterEntry;
038import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
039import org.ametys.plugins.repository.model.RepeaterDefinition;
040import org.ametys.runtime.model.ModelViewItemGroup;
041import org.ametys.runtime.model.ViewItemAccessor;
042import org.ametys.runtime.model.exception.BadItemTypeException;
043import org.ametys.runtime.model.exception.NotUniqueTypeException;
044import org.ametys.runtime.model.exception.UndefinedItemPathException;
045import org.ametys.runtime.model.exception.UnknownTypeException;
046import org.ametys.runtime.model.type.DataContext;
047
048/**
049 * Class for model aware repeaters
050 */
051public class ModelAwareRepeater implements Repeater
052{    
053    /** Definition of this repeater */
054    protected RepeaterDefinition _definition;
055    
056    /** Parent of the current {@link Repeater} */
057    protected ModelAwareDataHolder _parent;
058    
059    /** Root {@link DataHolder} */
060    protected ModelAwareDataHolder _root;
061    
062    /** Repository data to use to store entries in the repository */
063    protected RepositoryData _repositoryData;
064    
065    /**
066     * Creates a model aware repeater
067     * @param repositoryData the repository data of the repeater
068     * @param parent the parent of the created {@link Repeater}
069     * @param root the root {@link DataHolder}
070     * @param definition the definition of the repeater
071     */
072    public ModelAwareRepeater(RepositoryData repositoryData, ModelAwareDataHolder parent, ModelAwareDataHolder root, RepeaterDefinition definition)
073    {
074        _repositoryData = repositoryData;
075        _definition = definition;
076        _parent = parent;
077        _root = root;
078    }
079    
080    
081    public List<? extends ModelAwareRepeaterEntry> getEntries()
082    {
083        SortedSet<ModelAwareRepeaterEntry> entries = new TreeSet<>(new Comparator<ModelAwareRepeaterEntry>()
084        {
085            public int compare(ModelAwareRepeaterEntry entry1, ModelAwareRepeaterEntry entry2)
086            {
087                return Integer.compare(entry1.getPosition(), entry2.getPosition());
088            }
089        });
090        
091        for (String entryName : _repositoryData.getDataNames())
092        {
093            ModelAwareRepeaterEntry entry = getEntry(Integer.parseInt(entryName));
094            entries.add(entry);
095        }
096        
097        return Collections.unmodifiableList(new ArrayList<>(entries));
098    }
099    
100    public ModelAwareRepeaterEntry getEntry(int position)
101    {
102        if (1 <= position && position <= getSize())
103        {
104            RepositoryData entryRepositoryData = _repositoryData.getRepositoryData(String.valueOf(position));
105            return new ModelAwareRepeaterEntry(entryRepositoryData, this, _definition);
106        }
107        else if (-getSize() < position && position <= 0)
108        {
109            // Find the positive equivalent position and call the getEntry method with this position
110            return getEntry(getSize() + position);
111        }
112        else
113        {
114            return null;
115        }
116    }
117
118    public int getSize()
119    {
120        return _repositoryData.getDataNames().size();
121    }
122    
123    public boolean hasEntry(int position)
124    {
125        if (1 <= position)
126        {
127            return _repositoryData.hasValue(String.valueOf(position));
128        }
129        else
130        {
131            return _repositoryData.hasValue(String.valueOf(getSize() + position));
132        }
133    }
134    
135    /**
136     * Retrieves the repeater's model
137     * @return the repeater's model
138     */
139    public RepeaterDefinition getModel()
140    {
141        return _definition;
142    }
143    
144    public void dataToSAX(ContentHandler contentHandler, String dataPath, DataContext context) throws SAXException, IOException
145    {
146        for (ModelAwareRepeaterEntry entry : getEntries())
147        {
148            XMLUtils.startElement(contentHandler, "entry", _getEntryAttributes(entry));
149            entry.dataToSAX(contentHandler, dataPath, context);
150            XMLUtils.endElement(contentHandler, "entry");
151        }
152    }
153    
154    /**
155     * Generates SAX events for the data in the model of the current {@link DataHolder}
156     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
157     * @throws SAXException if an error occurs during the SAX events generation
158     * @throws IOException if an error occurs while reading a value using the I/O API
159     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
160     */
161    public void dataToSAX(ContentHandler contentHandler) throws SAXException, IOException, BadItemTypeException
162    {
163        dataToSAX(contentHandler, DataContext.newInstance());
164    }
165    
166    /**
167     * Generates SAX events for the data in the model of the current {@link DataHolder}
168     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
169     * @param context The context of the data to SAX
170     * @throws SAXException if an error occurs during the SAX events generation
171     * @throws IOException if an error occurs while reading a value using the I/O API
172     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
173     */
174    public void dataToSAX(ContentHandler contentHandler, DataContext context) throws SAXException, IOException, BadItemTypeException
175    {
176        ModelViewItemGroup viewItemGroup = ModelViewItemGroup.of(_definition);
177        dataToSAX(contentHandler, viewItemGroup, context);
178    }
179    
180    /**
181     * Generates SAX events for the data in the given view in the current {@link Repeater}
182     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
183     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items for which generate SAX events
184     * @throws SAXException if an error occurs during the SAX events generation
185     * @throws IOException if an error occurs while reading a value using the I/O API
186     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
187     */
188    public void dataToSAX(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor) throws SAXException, IOException, BadItemTypeException
189    {
190        dataToSAX(contentHandler, viewItemAccessor, DataContext.newInstance());
191    }
192    
193    /**
194     * Generates SAX events for the data in the given view in the current {@link Repeater}
195     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
196     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items for which generate SAX events
197     * @param context The context of the data to SAX
198     * @throws SAXException if an error occurs during the SAX events generation
199     * @throws IOException if an error occurs while reading a value using the I/O API
200     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
201     */
202    public void dataToSAX(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor, DataContext context) throws SAXException, IOException, BadItemTypeException
203    {
204        for (ModelAwareRepeaterEntry entry : getEntries())
205        {
206            XMLUtils.startElement(contentHandler, "entry", _getEntryAttributes(entry));
207            entry.dataToSAX(contentHandler, viewItemAccessor, context);
208            XMLUtils.endElement(contentHandler, "entry");
209        }
210    }
211    
212    private Attributes _getEntryAttributes(ModelAwareRepeaterEntry entry)
213    {
214        AttributesImpl entryAttrs = new AttributesImpl();
215        String entryName = Integer.toString(entry.getPosition());
216        entryAttrs.addCDATAAttribute("name", entryName);
217        return entryAttrs;
218    }
219    
220    /**
221     * Generates SAX events for the comments of the data in the given view in the current {@link DataHolder}
222     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
223     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items for which generate SAX events
224     * @throws SAXException if an error occurs during the SAX events generation
225     */
226    public void commentsToSAX(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor) throws SAXException
227    {
228        for (ModelAwareRepeaterEntry entry : getEntries())
229        {
230            entry.commentsToSAX(contentHandler, viewItemAccessor);
231        }
232    }
233    
234    public void copyTo(ModifiableRepeater repeater) throws UndefinedItemPathException, BadItemTypeException, UnknownTypeException, NotUniqueTypeException
235    {
236        for (RepeaterEntry entry : getEntries())
237        {
238            ModifiableRepeaterEntry entryDestination = repeater.addEntry(entry.getPosition());
239            entry.copyTo(entryDestination);
240        }
241    }
242    
243    public RepositoryData getRepositoryData()
244    {
245        return _repositoryData;
246    }
247    
248    public ModelAwareDataHolder getParentDataHolder()
249    {
250        return _parent;
251    }
252    
253    public ModelAwareDataHolder getRootDataHolder()
254    {
255        return _root;
256    }
257}