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.Collection; 020import java.util.Collections; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Objects; 025 026import org.apache.cocoon.ProcessingException; 027import org.apache.cocoon.xml.AttributesImpl; 028import org.apache.commons.lang3.StringUtils; 029import org.xml.sax.ContentHandler; 030import org.xml.sax.SAXException; 031 032import org.ametys.core.util.XMLUtils; 033import org.ametys.runtime.i18n.I18nizableText; 034import org.ametys.runtime.model.exception.BadItemTypeException; 035import org.ametys.runtime.util.ModifiableLabelable; 036 037/** 038 * View of items 039 */ 040public class View implements ViewItemContainer, ModifiableLabelable 041{ 042 private String _name; 043 private I18nizableText _label; 044 private I18nizableText _description; 045 private String _iconGlyph; 046 private String _iconDecorator; 047 private String _smallIcon; 048 private String _mediumIcon; 049 private String _largeIcon; 050 private boolean _isInternal; 051 052 private List<ViewItem> _items = new ArrayList<>(); 053 054 /** 055 * Creates a {@link View} with the items of the given {@link Model} 056 * @param model the model 057 * @return the created {@link View} 058 * @throws IllegalArgumentException if the model is <code>null</code> 059 */ 060 public static View of(Model model) throws IllegalArgumentException 061 { 062 if (model == null) 063 { 064 throw new IllegalArgumentException("Unable to create the view from a null model"); 065 } 066 else 067 { 068 return ViewHelper.createViewItemAccessor(List.of(model)); 069 } 070 } 071 072 /** 073 * Creates a {@link View} with the items of the given {@link Model}s 074 * @param models the models 075 * @return the created {@link View} 076 * @throws IllegalArgumentException if the models collection is empty 077 */ 078 public static View of(Collection<? extends Model> models) throws IllegalArgumentException 079 { 080 return ViewHelper.createViewItemAccessor(models); 081 } 082 083 /** 084 * Creates a {@link View} with the given items 085 * @param model the model containing items definitions 086 * @param itemPaths the paths of the items to put in the view 087 * @return the created {@link View} 088 * @throws IllegalArgumentException if the model is <code>null</code> or if an item path is <code>null</code>, empty, or is not defined in the given models 089 * @throws BadItemTypeException if a segment in a path (but not the last) does not represent a group item 090 */ 091 public static View of(Model model, String... itemPaths) throws IllegalArgumentException, BadItemTypeException 092 { 093 if (model == null) 094 { 095 throw new IllegalArgumentException("Unable to create the view from a null model"); 096 } 097 else 098 { 099 return ViewHelper.createViewItemAccessor(List.of(model), itemPaths); 100 } 101 } 102 103 /** 104 * Creates a {@link View} with the given items 105 * @param models the models containing items definitions 106 * @param itemPaths the paths of the items to put in the view 107 * @return the created {@link View} 108 * @throws IllegalArgumentException if the models collection is empty or if an item path is <code>null</code>, empty, or is not defined in the given models 109 * @throws BadItemTypeException if a segment in a path (but not the last) does not represent a group item 110 */ 111 public static View of(Collection<? extends Model> models, String... itemPaths) throws IllegalArgumentException, BadItemTypeException 112 { 113 return ViewHelper.createViewItemAccessor(models, itemPaths); 114 } 115 116 /** 117 * Creates a {@link View} with the given items. If the items are in a group, the hierarchy will be kept and the corresponding containers will be created 118 * @param modelItems the items to put in the view 119 * @return the created {@link View} 120 */ 121 public static View of(ModelItem... modelItems) 122 { 123 View view = new View(); 124 125 for (ModelItem modelItem : modelItems) 126 { 127 ViewHelper.addViewItem(modelItem.getPath(), view, modelItem.getModel()); 128 } 129 130 return view; 131 } 132 133 /** 134 * Copy the current view in the given one. 135 * Its view items are not copied 136 * @param view the copy 137 */ 138 public void copyTo(View view) 139 { 140 view.setName(getName()); 141 view.setLabel(getLabel()); 142 view.setDescription(getDescription()); 143 view.setInternal(isInternal()); 144 145 view.setIconGlyph(getIconGlyph()); 146 view.setIconDecorator(getIconDecorator()); 147 view.setSmallIcon(getSmallIcon()); 148 view.setMediumIcon(getMediumIcon()); 149 view.setLargeIcon(getLargeIcon()); 150 } 151 152 public String getName() 153 { 154 return _name; 155 } 156 157 public void setName(String name) 158 { 159 _name = name; 160 } 161 162 public I18nizableText getLabel() 163 { 164 return _label; 165 } 166 167 public void setLabel(I18nizableText label) 168 { 169 _label = label; 170 } 171 172 public I18nizableText getDescription() 173 { 174 return _description; 175 } 176 177 public void setDescription(I18nizableText description) 178 { 179 _description = description; 180 } 181 182 /** 183 * Retrieves the CSS class to use for glyph icon 184 * @return the glyph name. 185 */ 186 public String getIconGlyph() 187 { 188 return _iconGlyph; 189 } 190 191 /** 192 * Set the CSS class to use for glyph icon 193 * @param iconGlyph the glyph name. 194 */ 195 public void setIconGlyph(String iconGlyph) 196 { 197 _iconGlyph = iconGlyph; 198 } 199 200 /** 201 * Retrieves the CSS class to use for decorator above the main icon 202 * @return the glyph name. 203 */ 204 public String getIconDecorator() 205 { 206 return _iconDecorator; 207 } 208 209 /** 210 * Set the CSS class to use for decorator above the main icon 211 * @param iconDecorator the glyph name. 212 */ 213 public void setIconDecorator(String iconDecorator) 214 { 215 _iconDecorator = iconDecorator; 216 } 217 218 /** 219 * Retrieves the URL of the small icon without the context path. 220 * @return the icon URL for the small image 16x16. 221 */ 222 public String getSmallIcon() 223 { 224 return _smallIcon; 225 } 226 227 /** 228 * Set the URL of the small icon. 229 * @param smallIcon the URL of the small icon, without the context path. 230 */ 231 public void setSmallIcon(String smallIcon) 232 { 233 _smallIcon = smallIcon; 234 } 235 236 /** 237 * Retrieves the URL of the small icon without the context path. 238 * @return the icon URL for the medium sized image 32x32. 239 */ 240 public String getMediumIcon() 241 { 242 return _mediumIcon; 243 } 244 245 /** 246 * Set the URL of the medium icon. 247 * @param mediumIcon the URL of the medium icon, without the context path. 248 */ 249 public void setMediumIcon(String mediumIcon) 250 { 251 _mediumIcon = mediumIcon; 252 } 253 254 /** 255 * Retrieves the URL of the small icon without the context path. 256 * @return the icon URL for the large image 48x48. 257 */ 258 public String getLargeIcon() 259 { 260 return _largeIcon; 261 } 262 263 /** 264 * Set the URL of the large icon. 265 * @param largeIcon the URL of the large icon, without the context path. 266 */ 267 public void setLargeIcon(String largeIcon) 268 { 269 _largeIcon = largeIcon; 270 } 271 272 /** 273 * Determines if the view is for internal use only 274 * @return <code>true</code> if the view is for internal use only, <code>false</code> otherwise 275 */ 276 public boolean isInternal() 277 { 278 return _isInternal; 279 } 280 281 /** 282 * Set the internal status 283 * @param isInternal <code>true</code> to make the view for internal use only, <code>false</code> otherwise 284 */ 285 public void setInternal(boolean isInternal) 286 { 287 _isInternal = isInternal; 288 } 289 290 public List<ViewItem> getViewItems() 291 { 292 return Collections.unmodifiableList(this._items); 293 } 294 295 public void addViewItem(ViewItem item) 296 { 297 _items.add(item); 298 item.setParent(this); 299 } 300 301 public void insertViewItem(ViewItem item, int index) 302 { 303 if (index >= 0 && index <= _items.size()) 304 { 305 _items.add(index, item); 306 } 307 else 308 { 309 throw new IllegalArgumentException("Unable to insert an item at index " + index + ". This group contains " + _items.size() + " items."); 310 } 311 } 312 313 public boolean removeViewItem(ViewItem item) 314 { 315 return _items.remove(item); 316 } 317 318 public void clear() 319 { 320 _items.clear(); 321 } 322 323 /** 324 * Converts the view in a JSON map 325 * @param context the context of the definitions included in the view 326 * @return The view as a JSON map 327 * @throws ProcessingException If an error occurs when converting the view 328 */ 329 public Map<String, Object> toJSON(DefinitionContext context) throws ProcessingException 330 { 331 Map<String, Object> result = new LinkedHashMap<>(); 332 333 result.put("name", getName()); 334 result.put("internal", isInternal()); 335 result.put("label", getLabel()); 336 result.put("description", getDescription()); 337 338 result.put("icon-glyph", getIconGlyph()); 339 result.put("icon-decorator", getIconDecorator()); 340 result.put("small-icon-path", getSmallIcon()); 341 result.put("medium-icon-path", getMediumIcon()); 342 result.put("large-icon-path", getLargeIcon()); 343 344 result.put("elements", ViewHelper.viewItemsToJSON(getViewItems(), context)); 345 346 return result; 347 } 348 349 /** 350 * Generates SAX events for the view 351 * @param contentHandler the {@link ContentHandler} that will receive the SAX events 352 * @param context the context of the definitions included in the view 353 * @throws SAXException if an error occurs during the SAX events generation 354 */ 355 @SuppressWarnings("static-access") 356 public void toSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException 357 { 358 AttributesImpl attributes = new AttributesImpl(); 359 attributes.addCDATAAttribute("name", getName()); 360 attributes.addCDATAAttribute("internal", String.valueOf(isInternal())); 361 362 XMLUtils.startElement(contentHandler, "view", attributes); 363 364 XMLUtils.createI18nElementIfNotNull(contentHandler, "label", getLabel()); 365 XMLUtils.createI18nElementIfNotNull(contentHandler, "description", getDescription()); 366 367 XMLUtils.startElement(contentHandler, "icons"); 368 XMLUtils.createElementIfNotNull(contentHandler, "icon-glyph", getIconGlyph()); 369 XMLUtils.createElementIfNotNull(contentHandler, "icon-decorator", getIconDecorator()); 370 XMLUtils.createElementIfNotNull(contentHandler, "small-icon-path", getSmallIcon()); 371 XMLUtils.createElementIfNotNull(contentHandler, "medium-icon-path", getMediumIcon()); 372 XMLUtils.createElementIfNotNull(contentHandler, "large-icon-path", getLargeIcon()); 373 XMLUtils.endElement(contentHandler, "icons"); 374 375 for (ViewItem viewItem : getViewItems()) 376 { 377 viewItem.toSAX(contentHandler, context); 378 } 379 380 XMLUtils.endElement(contentHandler, "view"); 381 } 382 383 /** 384 * Include the given view to the current one. 385 * Add the items of the view to include if they are not already present in the current view 386 * @param viewToInclude the view to include 387 */ 388 public void includeView(View viewToInclude) 389 { 390 View referenceView = new View(); 391 referenceView.addViewItems(getViewItems()); 392 ViewHelper.addViewAccessorItems(this, viewToInclude, referenceView, StringUtils.EMPTY); 393 } 394 395 @Override 396 public int hashCode() 397 { 398 return Objects.hash(_items, _name); 399 } 400 401 @Override 402 public boolean equals(Object obj) 403 { 404 if (this == obj) 405 { 406 return true; 407 } 408 if (obj == null) 409 { 410 return false; 411 } 412 if (getClass() != obj.getClass()) 413 { 414 return false; 415 } 416 View other = (View) obj; 417 return Objects.equals(_items, other._items) && Objects.equals(_name, other._name); 418 } 419 420 /** 421 * Indicates whether some other object is "equal to" this one. 422 * @param obj the reference object with which to compare. 423 * @param checkDetails <code>true</code> to check the view's details during comparison (label, description, icon, ...) 424 * @return <code>true</code> if this object is the same as the given obj, <code>false</code> otherwise. 425 */ 426 public boolean equals(Object obj, boolean checkDetails) 427 { 428 if (!equals(obj)) 429 { 430 return false; 431 } 432 else if (checkDetails) 433 { 434 View other = (View) obj; 435 436 for (int i = 0; i < _items.size(); i++) 437 { 438 ViewItem item = _items.get(i); 439 ViewItem otherItem = other._items.get(i); 440 441 if (!item.equals(otherItem, checkDetails)) 442 { 443 return false; 444 } 445 } 446 447 return Objects.equals(_description, other._description) && Objects.equals(_iconDecorator, other._iconDecorator) && Objects.equals(_iconGlyph, other._iconGlyph) 448 && _isInternal == other._isInternal && Objects.equals(_label, other._label); 449 } 450 else 451 { 452 return true; 453 } 454 } 455}