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}