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