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 } 299 300 public void insertViewItem(ViewItem item, int index) 301 { 302 if (index >= 0 && index <= _items.size()) 303 { 304 _items.add(index, item); 305 } 306 else 307 { 308 throw new IllegalArgumentException("Unable to insert an item at index " + index + ". This group contains " + _items.size() + " items."); 309 } 310 } 311 312 public boolean removeViewItem(ViewItem item) 313 { 314 return _items.remove(item); 315 } 316 317 public void clear() 318 { 319 _items.clear(); 320 } 321 322 /** 323 * Converts the view in a JSON map 324 * @param context the context of the definitions included in the view 325 * @return The view as a JSON map 326 * @throws ProcessingException If an error occurs when converting the view 327 */ 328 public Map<String, Object> toJSON(DefinitionContext context) throws ProcessingException 329 { 330 Map<String, Object> result = new LinkedHashMap<>(); 331 332 result.put("name", getName()); 333 result.put("internal", isInternal()); 334 result.put("label", getLabel()); 335 result.put("description", getDescription()); 336 337 result.put("icon-glyph", getIconGlyph()); 338 result.put("icon-decorator", getIconDecorator()); 339 result.put("small-icon-path", getSmallIcon()); 340 result.put("medium-icon-path", getMediumIcon()); 341 result.put("large-icon-path", getLargeIcon()); 342 343 result.put("elements", ViewHelper.viewItemsToJSON(getViewItems(), context)); 344 345 return result; 346 } 347 348 /** 349 * Generates SAX events for the view 350 * @param contentHandler the {@link ContentHandler} that will receive the SAX events 351 * @param context the context of the definitions included in the view 352 * @throws SAXException if an error occurs during the SAX events generation 353 */ 354 @SuppressWarnings("static-access") 355 public void toSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException 356 { 357 AttributesImpl attributes = new AttributesImpl(); 358 attributes.addCDATAAttribute("name", getName()); 359 attributes.addCDATAAttribute("internal", String.valueOf(isInternal())); 360 361 XMLUtils.startElement(contentHandler, "view", attributes); 362 363 XMLUtils.createI18nElementIfNotNull(contentHandler, "label", getLabel()); 364 XMLUtils.createI18nElementIfNotNull(contentHandler, "description", getDescription()); 365 366 XMLUtils.startElement(contentHandler, "icons"); 367 XMLUtils.createElementIfNotNull(contentHandler, "icon-glyph", getIconGlyph()); 368 XMLUtils.createElementIfNotNull(contentHandler, "icon-decorator", getIconDecorator()); 369 XMLUtils.createElementIfNotNull(contentHandler, "small-icon-path", getSmallIcon()); 370 XMLUtils.createElementIfNotNull(contentHandler, "medium-icon-path", getMediumIcon()); 371 XMLUtils.createElementIfNotNull(contentHandler, "large-icon-path", getLargeIcon()); 372 XMLUtils.endElement(contentHandler, "icons"); 373 374 for (ViewItem viewItem : getViewItems()) 375 { 376 viewItem.toSAX(contentHandler, context); 377 } 378 379 XMLUtils.endElement(contentHandler, "view"); 380 } 381 382 /** 383 * Include the given view to the current one. 384 * Add the items of the view to include if they are not already present in the current view 385 * @param viewToInclude the view to include 386 */ 387 public void includeView(View viewToInclude) 388 { 389 View referenceView = new View(); 390 referenceView.addViewItems(getViewItems()); 391 ViewHelper.addViewAccessorItems(this, viewToInclude, referenceView, StringUtils.EMPTY); 392 } 393 394 @Override 395 public int hashCode() 396 { 397 return Objects.hash(_items, _name); 398 } 399 400 @Override 401 public boolean equals(Object obj) 402 { 403 if (this == obj) 404 { 405 return true; 406 } 407 if (obj == null) 408 { 409 return false; 410 } 411 if (getClass() != obj.getClass()) 412 { 413 return false; 414 } 415 View other = (View) obj; 416 return Objects.equals(_items, other._items) && Objects.equals(_name, other._name); 417 } 418 419 /** 420 * Indicates whether some other object is "equal to" this one. 421 * @param obj the reference object with which to compare. 422 * @param checkDetails <code>true</code> to check the view's details during comparison (label, description, icon, ...) 423 * @return <code>true</code> if this object is the same as the given obj, <code>false</code> otherwise. 424 */ 425 public boolean equals(Object obj, boolean checkDetails) 426 { 427 if (!equals(obj)) 428 { 429 return false; 430 } 431 else if (checkDetails) 432 { 433 View other = (View) obj; 434 435 for (int i = 0; i < _items.size(); i++) 436 { 437 ViewItem item = _items.get(i); 438 ViewItem otherItem = other._items.get(i); 439 440 if (!item.equals(otherItem, checkDetails)) 441 { 442 return false; 443 } 444 } 445 446 return Objects.equals(_description, other._description) && Objects.equals(_iconDecorator, other._iconDecorator) && Objects.equals(_iconGlyph, other._iconGlyph) 447 && _isInternal == other._isInternal && Objects.equals(_label, other._label); 448 } 449 else 450 { 451 return true; 452 } 453 } 454}