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}