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}