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