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