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.runtime.model; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.LinkedHashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.cocoon.ProcessingException; 028import org.apache.cocoon.xml.AttributesImpl; 029import org.xml.sax.ContentHandler; 030import org.xml.sax.SAXException; 031 032import org.ametys.core.util.XMLUtils; 033import org.ametys.runtime.model.type.DataContext; 034import org.ametys.runtime.model.type.ElementType; 035import org.ametys.runtime.model.type.ModelItemType; 036import org.ametys.runtime.model.type.ModelItemTypeConstants; 037 038/** 039 * Class for group of model items 040 */ 041public class ModelItemGroup extends AbstractModelItem implements ModelItemContainer 042{ 043 /** Id for model item group types */ 044 public static final String DEFAULT_TYPE_ID = "composite"; 045 046 private List<ModelItem> _children = new ArrayList<>(); 047 private ElementDefinition<Boolean> _switcher; 048 private ModelItemType _type; 049 050 /** 051 * Default constructor. 052 */ 053 public ModelItemGroup() 054 { 055 super(); 056 } 057 058 /** 059 * Constructor used to create simple models and items 060 * @param name the name of the definition 061 * @param children the group's children 062 */ 063 public ModelItemGroup(String name, ModelItem... children) 064 { 065 super(name); 066 Arrays.stream(children) 067 .forEach(item -> addChild(item)); 068 } 069 070 public Collection<ModelItem> getModelItems() 071 { 072 return getChildren(); 073 } 074 075 /** 076 * Retrieves the list of children model items 077 * @return the children 078 */ 079 public List<ModelItem> getChildren() 080 { 081 return getChildren(true); 082 } 083 084 /** 085 * Retrieves the list of children model items, with or without the switcher 086 * @param withSwitch true to retrieve the switcher with the other children 087 * @return the children with or without the switcher 088 */ 089 public List<ModelItem> getChildren(boolean withSwitch) 090 { 091 if (withSwitch || _switcher == null) 092 { 093 return Collections.unmodifiableList(_children); 094 } 095 096 List<ModelItem> childrenWithoutSwitcher = new ArrayList<>(_children); 097 childrenWithoutSwitcher.remove(_switcher); 098 return childrenWithoutSwitcher; 099 } 100 101 /** 102 * Add a child in the group 103 * @param child the item to add 104 */ 105 public void addChild(ModelItem child) 106 { 107 addChild(child, false); 108 } 109 110 /** 111 * Add a child in the group 112 * @param child the child to add 113 * @param isGroupSwitch true if the child to add is the group switch, false otherwise 114 */ 115 @SuppressWarnings("unchecked") 116 public void addChild(ModelItem child, boolean isGroupSwitch) 117 { 118 _children.add(child); 119 child.setParent(this); 120 121 if (isGroupSwitch) 122 { 123 if (_switcher == null) 124 { 125 if (child instanceof ElementDefinition) 126 { 127 ElementType type = ((ElementDefinition) child).getType(); 128 if (ModelItemTypeConstants.BOOLEAN_TYPE_ID.equals(type.getId())) 129 { 130 _switcher = (ElementDefinition<Boolean>) child; 131 } 132 } 133 134 if (_switcher == null) 135 { 136 // If switcher is still null, the type of child is not compatible 137 throw new RuntimeException("The group '" + getLabel() + "' has a switch '" + child + "' that is not valid because it is not a boolean."); 138 } 139 } 140 else 141 { 142 throw new RuntimeException("At least two group-switches have been defined for the configuration group '" + getLabel() + "'. These parameters are '" + _switcher + "' and '" + child + "'."); 143 } 144 } 145 } 146 147 /** 148 * Retrieves the switcher element definition 149 * @return the switcher 150 */ 151 public ElementDefinition<Boolean> getSwitcher() 152 { 153 return _switcher; 154 } 155 156 @Override 157 public void setModel(Model model) 158 { 159 super.setModel(model); 160 for (ModelItem modelItem : _children) 161 { 162 modelItem.setModel(model); 163 } 164 } 165 166 @Override 167 public ModelItemType getType() 168 { 169 return _type; 170 } 171 172 public void setType(ModelItemType type) 173 { 174 _type = type; 175 } 176 177 @Override 178 protected Map<String, Object> _toJSON(DefinitionContext context) throws ProcessingException 179 { 180 return _toJSON(context, true); 181 } 182 183 /** 184 * Converts the model item group in a JSON map 185 * @param context the context of the definition 186 * @param includeChildren true to iterate and add children as elements in the returned JSON map, false otherwise 187 * @return The model item as a JSON map 188 * @throws ProcessingException If an error occurs when converting the model item group 189 */ 190 public Map<String, Object> toJSON(DefinitionContext context, boolean includeChildren) throws ProcessingException 191 { 192 if (_shouldJSONBeEmpty(context)) 193 { 194 return Map.of(); 195 } 196 else 197 { 198 return _toJSON(context, includeChildren); 199 } 200 } 201 202 /** 203 * Converts the model item group in a JSON map 204 * @param context the context of the definition 205 * @param includeChildren true to iterate and add children as elements in the returned JSON map, false otherwise 206 * @return The model item as a JSON map 207 * @throws ProcessingException If an error occurs when converting the model item group 208 */ 209 protected Map<String, Object> _toJSON(DefinitionContext context, boolean includeChildren) throws ProcessingException 210 { 211 Map<String, Object> result = super._toJSON(context); 212 213 ModelItemType type = getType(); 214 result.put("type", type != null ? type.getId() : DEFAULT_TYPE_ID); 215 216 ElementDefinition<Boolean> switcher = getSwitcher(); 217 if (switcher != null) 218 { 219 Map<String, Object> switcherToJSON = new HashMap<>(); 220 221 switcherToJSON.put("id", switcher.getName()); 222 switcherToJSON.put("label", switcher.getLabel()); 223 224 if (switcher.getType() != null) 225 { 226 switcherToJSON.put("default-value", switcher.getType().valueToJSONForClient(switcher.getDefaultValue(), DataContext.newInstance())); 227 } 228 229 result.put("switcher", switcherToJSON); 230 } 231 232 // Do not include switcher in children because it is already included above 233 List<ModelItem> children = getChildren(false); 234 if (includeChildren && !children.isEmpty()) 235 { 236 Map<String, Object> elements = new LinkedHashMap<>(); 237 for (ModelItem child : children) 238 { 239 String name = child.getName(); 240 if (name != null) 241 { 242 elements.put(name, child.toJSON(context)); 243 } 244 } 245 246 result.put("elements", elements); 247 } 248 249 return result; 250 } 251 252 /** 253 * {@inheritDoc} 254 * Do not generate SAX events for children. 255 * This method has been created to generate SAX events for views, the children are taken into account via view items children 256 */ 257 @SuppressWarnings("static-access") 258 @Override 259 public void toSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException 260 { 261 super.toSAX(contentHandler, context); 262 263 ElementDefinition<Boolean> switcher = getSwitcher(); 264 if (switcher != null) 265 { 266 AttributesImpl switcherAttributes = new AttributesImpl(); 267 switcherAttributes.addCDATAAttribute("name", switcher.getName()); 268 269 XMLUtils.startElement(contentHandler, "switcher", switcherAttributes); 270 XMLUtils.createI18nElementIfNotNull(contentHandler, "label", switcher.getLabel()); 271 272 if (switcher.getType() != null) 273 { 274 switcher.getType().valueToSAX(contentHandler, "default-value", switcher.getDefaultValue(), DataContext.newInstance()); 275 } 276 277 XMLUtils.endElement(contentHandler, "switcher"); 278 } 279 } 280 281 /** 282 * Creates a {@link ModelItemGroup} 283 * @param name the group's name 284 * @param children the group's children 285 * @return the created {@link ModelItemGroup} 286 */ 287 public static ModelItemGroup of(String name, ModelItem... children) 288 { 289 return new ModelItemGroup(name, children); 290 } 291}