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; 017 018import java.util.Collection; 019import java.util.Map; 020import java.util.Objects; 021import java.util.Optional; 022 023import javax.jcr.RepositoryException; 024import javax.jcr.Session; 025 026import org.apache.commons.collections4.CollectionUtils; 027import org.xml.sax.ContentHandler; 028import org.xml.sax.SAXException; 029 030import org.ametys.cms.data.type.BaseContentElementType; 031import org.ametys.cms.repository.ModifiableContent; 032import org.ametys.plugins.repository.AmetysObjectResolver; 033import org.ametys.plugins.repository.AmetysRepositoryException; 034import org.ametys.plugins.repository.UnknownAmetysObjectException; 035import org.ametys.plugins.repository.data.external.ExternalizableDataProvider.ExternalizableDataStatus; 036import org.ametys.plugins.repository.data.holder.ModifiableDataHolder; 037import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder; 038import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareComposite; 039import org.ametys.plugins.repository.data.holder.group.ModifiableRepeater; 040import org.ametys.plugins.repository.data.holder.values.SynchronizationContext; 041import org.ametys.plugins.repository.data.holder.values.SynchronizationResult; 042import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 043import org.ametys.runtime.model.ModelItem; 044import org.ametys.runtime.model.ModelItemContainer; 045import org.ametys.runtime.model.ViewItemAccessor; 046import org.ametys.runtime.model.exception.BadDataPathCardinalityException; 047import org.ametys.runtime.model.exception.BadItemTypeException; 048import org.ametys.runtime.model.exception.UndefinedItemPathException; 049import org.ametys.runtime.model.type.DataContext; 050import org.ametys.runtime.model.type.ModelItemType; 051 052import com.fasterxml.jackson.annotation.JsonIdentityInfo; 053import com.fasterxml.jackson.annotation.JsonIdentityReference; 054import com.fasterxml.jackson.annotation.ObjectIdGenerators; 055 056/** 057 * Content wrapper used by attributes of type content 058 * @see BaseContentElementType 059 */ 060@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "contentId") 061@JsonIdentityReference(alwaysAsId = true) 062public class ContentValue implements ModifiableModelAwareDataHolder 063{ 064 private AmetysObjectResolver _resolver; 065 private String _contentId; 066 private ModifiableContent _content; 067 private Session _session; 068 069 /** 070 * Constructor of the content wrapper 071 * @param content the existing content 072 */ 073 public ContentValue(ModifiableContent content) 074 { 075 _content = content; 076 _contentId = content.getId(); 077 } 078 079 /** 080 * Constructor of the content wrapper 081 * @param resolver resolver used to get the content from its identifier 082 * @param contentId content identifier 083 */ 084 public ContentValue(AmetysObjectResolver resolver, String contentId) 085 { 086 this(resolver, contentId, null); 087 } 088 089 /** 090 * Constructor of the content wrapper 091 * @param resolver resolver used to get the content from its identifier 092 * @param contentId content identifier 093 * @param session the current session. If <code>null</code>, a new session will be used to retrieve the content 094 */ 095 public ContentValue(AmetysObjectResolver resolver, String contentId, Session session) 096 { 097 _resolver = resolver; 098 _contentId = contentId; 099 _session = session; 100 } 101 102 /** 103 * Retrieves the content's identifier 104 * @return the content's identifier 105 */ 106 public String getContentId() 107 { 108 return _contentId; 109 } 110 111 /** 112 * Retrieves the content 113 * @return the content 114 * @throws AmetysRepositoryException if an error occurs. 115 * @throws UnknownAmetysObjectException if no content exists for the identifier 116 */ 117 public ModifiableContent getContent() throws AmetysRepositoryException, UnknownAmetysObjectException 118 { 119 if (_content == null) 120 { 121 if (_session != null) 122 { 123 try 124 { 125 _content = _resolver.resolveById(_contentId, _session); 126 } 127 catch (RepositoryException e) 128 { 129 throw new AmetysRepositoryException("Unable to retrieve the content with the id '" + _contentId + "'.", e); 130 } 131 } 132 else 133 { 134 _content = _resolver.resolveById(_contentId); 135 } 136 } 137 138 return _content; 139 } 140 141 /** 142 * Retrieves an {@link Optional} describing the content, or an empty {@link Optional} if the content does not exist 143 * @return an {@link Optional} describing the content 144 */ 145 public Optional<ModifiableContent> getContentIfExists() 146 { 147 try 148 { 149 return Optional.ofNullable(getContent()); 150 } 151 catch (AmetysRepositoryException e) 152 { 153 return Optional.empty(); 154 } 155 } 156 157 @Override 158 public int hashCode() 159 { 160 return Objects.hash(_contentId); 161 } 162 163 @Override 164 public boolean equals(Object obj) 165 { 166 if (!(obj instanceof ContentValue)) 167 { 168 return false; 169 } 170 171 return Objects.equals(_contentId, ((ContentValue) obj)._contentId); 172 } 173 174 public boolean hasValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException 175 { 176 return getContentIfExists() 177 .map(c -> c.hasValue(dataPath)) 178 .orElse(false); 179 } 180 181 public boolean hasValue(String dataPath, String dataTypeId) throws IllegalArgumentException, BadDataPathCardinalityException 182 { 183 return getContentIfExists() 184 .map(c -> c.hasValue(dataPath, dataTypeId)) 185 .orElse(false); 186 } 187 188 public boolean hasLocalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException 189 { 190 return getContentIfExists() 191 .map(c -> c.hasLocalValue(dataPath)) 192 .orElse(false); 193 } 194 195 public boolean hasExternalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException 196 { 197 return getContentIfExists() 198 .map(c -> c.hasExternalValue(dataPath)) 199 .orElse(false); 200 } 201 202 public boolean hasValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException 203 { 204 return getContentIfExists() 205 .map(c -> c.hasValueOrEmpty(dataPath)) 206 .orElse(false); 207 } 208 209 public boolean hasLocalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException 210 { 211 return getContentIfExists() 212 .map(c -> c.hasLocalValueOrEmpty(dataPath)) 213 .orElse(false); 214 } 215 216 public boolean hasExternalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException 217 { 218 return getContentIfExists() 219 .map(c -> c.hasExternalValueOrEmpty(dataPath)) 220 .orElse(false); 221 } 222 223 public Collection<String> getDataNames() 224 { 225 return getContentIfExists() 226 .map(c -> c.getDataNames()) 227 .orElse(CollectionUtils.EMPTY_COLLECTION); 228 } 229 230 public <T> T getValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 231 { 232 return getContentIfExists() 233 .map(c -> c.<T>getValue(dataPath)) 234 .orElse(null); 235 } 236 237 public <T> T getLocalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 238 { 239 return getContentIfExists() 240 .map(c -> c.<T>getLocalValue(dataPath)) 241 .orElse(null); 242 } 243 244 public <T> T getExternalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 245 { 246 return getContentIfExists() 247 .map(c -> c.<T>getExternalValue(dataPath)) 248 .orElse(null); 249 } 250 251 public <T> T getValueOrDefault(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 252 { 253 return getContentIfExists() 254 .map(c -> c.<T>getValueOrDefault(dataPath)) 255 .orElse(null); 256 } 257 258 public <T> T getValueOrDefault(String dataPath, T defaultValue) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 259 { 260 return getContentIfExists() 261 .map(c -> c.<T>getValueOrDefault(dataPath, defaultValue)) 262 .orElse(defaultValue); 263 } 264 265 public <T> T getValueOfType(String dataPath, String dataTypeId) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 266 { 267 return getContentIfExists() 268 .map(c -> c.<T>getValueOfType(dataPath, dataTypeId)) 269 .orElse(null); 270 } 271 272 public <T> T getValueOfTypeOrDefault(String dataPath, String dataTypeId, T defaultValue) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 273 { 274 return getContentIfExists() 275 .map(c -> c.<T>getValueOfTypeOrDefault(dataPath, dataTypeId, defaultValue)) 276 .orElse(defaultValue); 277 } 278 279 public <T> T getValueWithMultiValuedPathSegments(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 280 { 281 return getContentIfExists() 282 .map(c -> c.<T>getValueWithMultiValuedPathSegments(dataPath)) 283 .orElse(null); 284 } 285 286 public ExternalizableDataStatus getStatus(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 287 { 288 return getContentIfExists() 289 .map(c -> c.getStatus(dataPath)) 290 .orElse(ExternalizableDataStatus.LOCAL); 291 } 292 293 public boolean isMultiple(String dataPath) throws IllegalArgumentException, UndefinedItemPathException 294 { 295 return getContentIfExists() 296 .map(c -> c.isMultiple(dataPath)) 297 .orElse(false); 298 } 299 300 public boolean isMultiple(String dataPath, String dataTypeId) throws IllegalArgumentException, UndefinedItemPathException 301 { 302 return getContentIfExists() 303 .map(c -> c.isMultiple(dataPath, dataTypeId)) 304 .orElse(false); 305 } 306 307 public <X extends ModelItemType> X getType(String dataPath) throws IllegalArgumentException, UndefinedItemPathException 308 { 309 return getContentIfExists() 310 .map(c -> c.<X>getType(dataPath)) 311 .orElse(null); 312 } 313 314 public Collection< ? extends ModelItemContainer> getModel() 315 { 316 return getContentIfExists() 317 .map(c -> c.getModel()) 318 .orElse(null); 319 } 320 321 public ModelItem getDefinition(String path) throws IllegalArgumentException, UndefinedItemPathException 322 { 323 return getContentIfExists() 324 .map(c -> c.getDefinition(path)) 325 .orElse(null); 326 } 327 328 public boolean hasDefinition(String path) throws IllegalArgumentException 329 { 330 return getContentIfExists() 331 .map(c -> c.hasDefinition(path)) 332 .orElse(null); 333 } 334 335 public ModifiableModelAwareComposite getComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 336 { 337 return getContentIfExists() 338 .map(c -> c.getComposite(compositePath)) 339 .orElse(null); 340 } 341 342 public ModifiableModelAwareComposite getLocalComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 343 { 344 return getContentIfExists() 345 .map(c -> c.getLocalComposite(compositePath)) 346 .orElse(null); 347 } 348 349 public ModifiableModelAwareComposite getExternalComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 350 { 351 return getContentIfExists() 352 .map(c -> c.getExternalComposite(compositePath)) 353 .orElse(null); 354 } 355 356 public ModifiableRepeater getRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 357 { 358 return getContentIfExists() 359 .map(c -> c.getRepeater(repeaterPath)) 360 .orElse(null); 361 } 362 363 public ModifiableRepeater getLocalRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 364 { 365 return getContentIfExists() 366 .map(c -> c.getLocalRepeater(repeaterPath)) 367 .orElse(null); 368 } 369 370 public ModifiableRepeater getExternalRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 371 { 372 return getContentIfExists() 373 .map(c -> c.getExternalRepeater(repeaterPath)) 374 .orElse(null); 375 } 376 377 public void copyTo(ModifiableDataHolder dataHolder) throws BadItemTypeException, UndefinedItemPathException 378 { 379 getContent().copyTo(dataHolder); 380 } 381 382 public void copyTo(ModifiableDataHolder dataHolder, DataContext context) throws BadItemTypeException, UndefinedItemPathException 383 { 384 getContent().copyTo(dataHolder, context); 385 } 386 387 public void dataToSAX(ContentHandler contentHandler, String dataPath, DataContext context) throws SAXException, BadItemTypeException, UndefinedItemPathException 388 { 389 getContent().dataToSAX(contentHandler, dataPath, context); 390 } 391 392 public void dataToSAX(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor, DataContext context) throws SAXException, BadItemTypeException 393 { 394 getContent().dataToSAX(contentHandler, viewItemAccessor, context); 395 } 396 397 public void dataToSAXForEdition(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor, DataContext context) throws SAXException, BadItemTypeException 398 { 399 getContent().dataToSAXForEdition(contentHandler, viewItemAccessor, context); 400 } 401 402 public Object dataToJSON(String dataPath, DataContext context) throws BadItemTypeException, UndefinedItemPathException 403 { 404 return getContent().dataToJSON(dataPath, context); 405 } 406 407 public Map<String, Object> dataToJSON(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException 408 { 409 return getContent().dataToJSON(viewItemAccessor, context); 410 } 411 412 public Map<String, Object> dataToJSONForEdition(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException 413 { 414 return getContent().dataToJSONForEdition(viewItemAccessor, context); 415 } 416 417 public Map<String, Object> dataToMap(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException 418 { 419 return getContent().dataToMap(viewItemAccessor, context); 420 } 421 422 public boolean hasDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException 423 { 424 return getContent().hasDifferences(viewItemAccessor, values); 425 } 426 427 public boolean hasDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException 428 { 429 return getContent().hasDifferences(viewItemAccessor, values, context); 430 } 431 432 public Collection<ModelItem> getDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException 433 { 434 return getContent().getDifferences(viewItemAccessor, values); 435 } 436 437 public Collection<ModelItem> getDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException 438 { 439 return getContent().getDifferences(viewItemAccessor, values, context); 440 } 441 442 public ModifiableModelAwareComposite getComposite(String compositePath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 443 { 444 return getContentIfExists() 445 .map(c -> c.getComposite(compositePath, createNew)) 446 .orElse(null); 447 } 448 449 public ModifiableModelAwareComposite getLocalComposite(String compositePath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 450 { 451 return getContentIfExists() 452 .map(c -> c.getLocalComposite(compositePath, createNew)) 453 .orElse(null); 454 } 455 456 public ModifiableModelAwareComposite getExternalComposite(String compositePath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 457 { 458 return getContentIfExists() 459 .map(c -> c.getExternalComposite(compositePath, createNew)) 460 .orElse(null); 461 } 462 463 public ModifiableRepeater getRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 464 { 465 return getContentIfExists() 466 .map(c -> c.getRepeater(repeaterPath, createNew)) 467 .orElse(null); 468 } 469 470 public ModifiableRepeater getLocalRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 471 { 472 return getContentIfExists() 473 .map(c -> c.getLocalRepeater(repeaterPath, createNew)) 474 .orElse(null); 475 } 476 477 public ModifiableRepeater getExternalRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 478 { 479 return getContentIfExists() 480 .map(c -> c.getExternalRepeater(repeaterPath, createNew)) 481 .orElse(null); 482 } 483 484 public <T extends SynchronizationResult> T synchronizeValues(Map<String, Object> values) throws BadItemTypeException, UndefinedItemPathException 485 { 486 return getContent().synchronizeValues(values); 487 } 488 489 public <T extends SynchronizationResult> T synchronizeValues(Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException, UndefinedItemPathException 490 { 491 return getContent().synchronizeValues(values, context); 492 } 493 494 public <T extends SynchronizationResult> T synchronizeValues(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException, UndefinedItemPathException 495 { 496 return getContent().synchronizeValues(viewItemAccessor, values); 497 } 498 499 public <T extends SynchronizationResult> T synchronizeValues(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException, UndefinedItemPathException 500 { 501 return getContent().synchronizeValues(viewItemAccessor, values, context); 502 } 503 504 public void setValue(String dataPath, Object value) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 505 { 506 getContent().setValue(dataPath, value); 507 } 508 509 public void setLocalValue(String dataPath, Object localValue) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 510 { 511 getContent().setLocalValue(dataPath, localValue); 512 } 513 514 public void setExternalValue(String dataPath, Object externalValue) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 515 { 516 getContent().setExternalValue(dataPath, externalValue); 517 } 518 519 public void setValue(String dataPath, Object value, String dataTypeId) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 520 { 521 getContent().setValue(dataPath, value, dataTypeId); 522 } 523 524 public void setStatus(String dataPath, ExternalizableDataStatus status) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 525 { 526 getContent().setStatus(dataPath, status); 527 } 528 529 public void removeValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 530 { 531 getContent().removeValue(dataPath); 532 } 533 534 public void removeLocalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 535 { 536 getContent().removeLocalValue(dataPath); 537 } 538 539 public void removeExternalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 540 { 541 getContent().removeExternalValue(dataPath); 542 } 543 544 public void removeExternalizableMetadataIfExists(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException 545 { 546 getContent().removeExternalizableMetadataIfExists(dataPath); 547 } 548 549 public ModifiableRepositoryData getRepositoryData() 550 { 551 return getContent().getRepositoryData(); 552 } 553 554 public Optional<? extends ModifiableModelAwareDataHolder> getParentDataHolder() 555 { 556 return getContent().getParentDataHolder(); 557 } 558 559 public ModifiableModelAwareDataHolder getRootDataHolder() 560 { 561 return getContent().getRootDataHolder(); 562 } 563 564 @Override 565 public String toString() 566 { 567 return _contentId; 568 } 569}