/*
* Copyright (C) 2005-2012 BetaCONCEPT Limited
*
* This file is part of Astroboa.
*
* Astroboa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Astroboa is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Astroboa. If not, see <http://www.gnu.org/licenses/>.
*/
package org.betaconceptframework.astroboa.engine.model.lazy.local;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.BinaryProperty;
import org.betaconceptframework.astroboa.api.model.CmsProperty;
import org.betaconceptframework.astroboa.api.model.CmsRepositoryEntity;
import org.betaconceptframework.astroboa.api.model.ComplexCmsProperty;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.ObjectReferenceProperty;
import org.betaconceptframework.astroboa.api.model.SimpleCmsProperty;
import org.betaconceptframework.astroboa.api.model.Topic;
import org.betaconceptframework.astroboa.api.model.ValueType;
import org.betaconceptframework.astroboa.api.model.definition.BinaryPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.CmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.ComplexCmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.ContentObjectTypeDefinition;
import org.betaconceptframework.astroboa.api.model.definition.SimpleCmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.StringPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.model.query.render.RenderProperties;
import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
import org.betaconceptframework.astroboa.engine.jcr.renderer.BinaryChannelRenderer;
import org.betaconceptframework.astroboa.engine.jcr.renderer.CmsRepositoryEntityRenderer;
import org.betaconceptframework.astroboa.engine.jcr.renderer.ContentObjectRenderer;
import org.betaconceptframework.astroboa.engine.jcr.renderer.TopicRenderer;
import org.betaconceptframework.astroboa.engine.jcr.util.CmsRepositoryEntityUtils;
import org.betaconceptframework.astroboa.engine.jcr.util.JcrValueUtils;
import org.betaconceptframework.astroboa.engine.jcr.util.VersionUtils;
import org.betaconceptframework.astroboa.model.impl.BinaryPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.BooleanPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.CalendarPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.CmsRepositoryEntityImpl;
import org.betaconceptframework.astroboa.model.impl.ComplexCmsPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.DoublePropertyImpl;
import org.betaconceptframework.astroboa.model.impl.LazyCmsProperty;
import org.betaconceptframework.astroboa.model.impl.LongPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.ObjectReferencePropertyImpl;
import org.betaconceptframework.astroboa.model.impl.StringPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.TopicReferencePropertyImpl;
import org.betaconceptframework.astroboa.model.impl.definition.ComplexCmsPropertyDefinitionImpl;
import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem;
import org.betaconceptframework.astroboa.model.impl.item.CmsReadOnlyItem;
import org.betaconceptframework.astroboa.model.impl.item.JcrBuiltInItem;
import org.betaconceptframework.astroboa.util.DateUtils;
import org.betaconceptframework.astroboa.util.PropertyPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Gregory Chomatas (gchomatas@betaconcept.com)
* @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
*
*/
public class LazyComplexCmsPropertyLoader {
private final Logger logger = LoggerFactory.getLogger(LazyComplexCmsPropertyLoader.class);
@Autowired
private BinaryChannelRenderer binaryChannelRenderer;
@Autowired
private CmsRepositoryEntityUtils cmsRepositoryEntityUtils;
@Autowired
private CmsRepositoryEntityRenderer cmsRepositoryEntityRenderer;
@Autowired
private VersionUtils versionUtils;
@Autowired
private TopicRenderer topicRenderer;
@Autowired
private ContentObjectRenderer contentObjectRenderer;
public List<CmsProperty<?, ?>> renderChildProperty(CmsPropertyDefinition currentChildPropertyDefinition,
String jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty, String jcrNodeUUIDWhichCorrespondsToContentObejct,
RenderProperties renderProperties, Session session,
Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities) {
try{
if (currentChildPropertyDefinition == null){
logger.warn("No cms property definition is provided for parent node UUID {} and content object node UUID {}",
jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty, jcrNodeUUIDWhichCorrespondsToContentObejct);
return null;
}
if (logger.isDebugEnabled()){
logger.debug("Lazy render property {}",
currentChildPropertyDefinition.getFullPath());
}
//Load property container node if a UUID is provided
//Otherwise a blank template for this child property will be created
Node propertyContainerNode = null;
if (StringUtils.isNotBlank(jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty)){
propertyContainerNode = session.getNodeByIdentifier(jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty);
}
if (cachedCmsRepositoryEntities == null){
cachedCmsRepositoryEntities = new HashMap<String, CmsRepositoryEntity>();
}
if (currentChildPropertyDefinition instanceof ComplexCmsPropertyDefinition)
return renderComplexProperty(currentChildPropertyDefinition.getName(), propertyContainerNode, currentChildPropertyDefinition);
else{
CmsProperty<?, ?> simpleCmsProperty = renderSimpleProperty(currentChildPropertyDefinition.getName(),
currentChildPropertyDefinition, propertyContainerNode,
session, jcrNodeUUIDWhichCorrespondsToContentObejct,
cachedCmsRepositoryEntities, renderProperties);
List<CmsProperty<?,?>> childCmsProperties = new ArrayList<CmsProperty<?,?>>();
if (simpleCmsProperty != null){
childCmsProperties.add(simpleCmsProperty);
}
return childCmsProperties;
}
}
catch (Exception e)
{
logger.error("While trying to lazy render child property {} with property container jcr node UUID {} " +
" and content object jcr node UUID {}", new Object[]{currentChildPropertyDefinition.getFullPath(),
jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty,
jcrNodeUUIDWhichCorrespondsToContentObejct});
throw new CmsException(e);
}
}
private CmsProperty createNewCmsProperty(CmsPropertyDefinition propertyDefinition,String propertyName) {
CmsProperty newProperty = newCmsProperty(propertyDefinition.getValueType());
if (newProperty instanceof SimpleCmsProperty)
((SimpleCmsProperty)newProperty).setPropertyDefinition((SimpleCmsPropertyDefinition)propertyDefinition);
else if (newProperty instanceof ComplexCmsProperty){
//In case definition refers to its parent
//all its children must be initialized
((ComplexCmsPropertyDefinitionImpl)propertyDefinition).checkIfRecursiveAndCloneParentChildDefinitions();
((ComplexCmsProperty)newProperty).setPropertyDefinition((ComplexCmsPropertyDefinition)propertyDefinition);
}
return newProperty;
}
private CmsProperty<?,?> newCmsProperty(ValueType valueType) {
CmsProperty newProperty = null;
switch (valueType) {
case Binary:
newProperty = new BinaryPropertyImpl();
break;
case Boolean:
newProperty = new BooleanPropertyImpl();
break;
case Date:
newProperty = new CalendarPropertyImpl();
break;
case Complex:{
newProperty = new ComplexCmsPropertyImpl();
break;
}
case ObjectReference:
newProperty = new ObjectReferencePropertyImpl();
break;
case Double:
newProperty = new DoublePropertyImpl();
break;
case Long:
newProperty = new LongPropertyImpl();
break;
case String:
newProperty = new StringPropertyImpl();
break;
case TopicReference:
newProperty = new TopicReferencePropertyImpl();
break;
default:
return null;
}
if (newProperty != null){
((CmsRepositoryEntityImpl)newProperty).setAuthenticationToken((AstroboaClientContextHolder.getActiveClientContext() != null ?
AstroboaClientContextHolder.getActiveClientContext().getAuthenticationToken() : null));
}
return newProperty;
}
private CmsProperty<?,?> renderSimpleProperty(String childPropertyName, CmsPropertyDefinition currentChildPropertyDefinition, Node propertyContainerNode, Session session, String contentObjectNodeUUID, Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, RenderProperties renderProperties) throws RepositoryException {
CmsProperty<?,?> simpleProperty = createNewCmsProperty(currentChildPropertyDefinition, childPropertyName);
//Render values
if (simpleProperty != null){
if (currentChildPropertyDefinition instanceof BinaryPropertyDefinition) {
renderBinaryChannels((BinaryProperty)simpleProperty,propertyContainerNode,
cachedCmsRepositoryEntities, session,renderProperties);
}
else{
renderValueForSimpleProperty((SimpleCmsProperty<?,?,?>)simpleProperty,session, contentObjectNodeUUID, propertyContainerNode, cachedCmsRepositoryEntities, renderProperties);
}
}
return simpleProperty;
}
private void renderValueForSimpleProperty(SimpleCmsProperty<?,?,?> simpleContentObjectProperty, Session session, String contentObjectNodeUUID,
Node propertyContainerNode, Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, RenderProperties renderProperties) throws RepositoryException {
final String propertyName = simpleContentObjectProperty.getName();
//Special case. In order to render versions and hasVersion we need
//content object to look in VersionHistory
if ( (CmsReadOnlyItem.Versions.getJcrName().equals(propertyName) || CmsReadOnlyItem.HasVersion.getJcrName().equals(propertyName))){
Node contentObjectNode = null;
try{
//This is meaningful only if contentObject UUID is provided
if (StringUtils.isNotBlank(contentObjectNodeUUID)){
contentObjectNode = session.getNodeByIdentifier(contentObjectNodeUUID);
if (contentObjectNode != null){
renderVersionNames(simpleContentObjectProperty, contentObjectNode, session);
return;
}
else{
throw new Exception("Null content object node");
}
}
}
catch(Exception e){
logger.warn("Could not render property '"+propertyName+ "' Property Container Node : "+ propertyContainerNode.getPath() +
" Content Object Node :" + (contentObjectNode != null ? contentObjectNode.getPath() : " no content object node ")+
" Content object node UUID "+contentObjectNodeUUID, e);
}
}
else if (propertyContainerNode == null || !propertyContainerNode.hasProperty(propertyName)){
//Check if property is Mandatory
SimpleCmsPropertyDefinition<?> propertyDefinition = (SimpleCmsPropertyDefinition<?>) simpleContentObjectProperty.getPropertyDefinition();
//Issue a warning only if there is a container node
//In cases where content object is new there is no property container node yet
//therefore warning is misleading
if (propertyDefinition.isMandatory()){
if (propertyContainerNode != null){
logger.warn("Mandatory property {} does not exist for content object {}", propertyDefinition.getFullPath(), contentObjectNodeUUID);
}
//Return default value
//Property does not exist, it is mandatory and therefore render its default value
/*
* Default value is not set at all during read
* It is automatically set upon save or update
* only when property is mandatory
if (propertyDefinition.getDefaultValue() != null){
renderSimpleValue(simpleContentObjectProperty, propertyDefinition.getDefaultValue(),session, cachedCmsRepositoryEntities, locale, renderProperties);
}
*/
}
}
else{
renderSimpleCmsProperty(simpleContentObjectProperty, propertyContainerNode.getProperty(propertyName),cachedCmsRepositoryEntities,session, renderProperties);
}
}
private void renderBinaryChannels(BinaryProperty binaryProperty, Node propertyContainerNode, Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, Session session, RenderProperties renderProperties) throws RepositoryException {
String propertyName = binaryProperty.getName();
//Binary channels are sub nodes of property node
if (propertyContainerNode == null || (
(!propertyContainerNode.hasNode(propertyName)
&& !propertyContainerNode.hasProperty(propertyName))) //it may be the case that binary channel is unmanaged, thus it is stored as a jcr property
){
//Issue a warning only if there is a container node
//In cases where content object is new there is no property container node yet
//therefore warning is misleading
if (binaryProperty.getPropertyDefinition().isMandatory() && propertyContainerNode != null){
logger.warn("Mandatory property {} does not exist", propertyName);
}
}
else{
if (propertyContainerNode.hasProperty(propertyName)){
//Binary Channel is unmanaged. Only relative system paths are stored as values
//of a jcr property
//Check that binary property's definition states that binary property must contain
//unmanaged binary channels
if (binaryProperty.getPropertyDefinition() == null ||
! binaryProperty.getPropertyDefinition().isBinaryChannelUnmanaged()){
logger.warn("Property {} is not defined as unmanaged binary property but only relative path(s) have been found");
}
else{
renderSimpleCmsProperty(binaryProperty, propertyContainerNode.getProperty(propertyName),
cachedCmsRepositoryEntities, session, renderProperties);
}
}
else{
NodeIterator nodeIter = propertyContainerNode.getNodes(propertyName);
while (nodeIter.hasNext())
{
Node node = nodeIter.nextNode();
if (node.isNodeType(CmsBuiltInItem.BinaryChannel.getJcrName()) ||
//If it is a frozen Node
//it must have a property named jcr:frozenPrimaryType whose
//value must be CmsBuiltInItem.BinaryChannel.getJcrName()
(node.isNodeType(JcrBuiltInItem.NtFrozenNode.getJcrName()) &&
node.hasProperty(JcrBuiltInItem.JcrFrozenPrimaryType.getJcrName()) &&
node.getProperty(JcrBuiltInItem.JcrFrozenPrimaryType.getJcrName()).getString().equals(CmsBuiltInItem.BinaryChannel.getJcrName())))
binaryProperty.addSimpleTypeValue(binaryChannelRenderer.render(node, false));
else
logger.warn("Binary channel {} exists in content object node {} with type other than {} and that is {}",
new Object[]{propertyName, propertyContainerNode.getPath(), CmsBuiltInItem.BinaryChannel.getJcrName(),node.getPrimaryNodeType().getName()});
}
}
}
}
private List<CmsProperty<?,?>> renderComplexProperty(String childPropertyName, Node propertyContainerNode, CmsPropertyDefinition currentChildPropertyDefinition) throws RepositoryException {
List<CmsProperty<?,?>> childCmsProperties = new ArrayList<CmsProperty<?,?>>();
if (propertyContainerNode == null || !propertyContainerNode.hasNode(childPropertyName)){
//No complex property node exist in repository
//If mandatory create a new complex property and issue a warning
//Issue a warning only if there is a container node
//In cases where content object is new there is no property container node yet
//therefore warning is misleading
if (currentChildPropertyDefinition.isMandatory() && propertyContainerNode != null) {
logger.warn("Mandatory property {} does not exist in repository ", childPropertyName);
}
//Create an empty property
childCmsProperties.add(createNewCmsProperty(currentChildPropertyDefinition, childPropertyName));
}
else
{
NodeIterator complexNodes = propertyContainerNode.getNodes(childPropertyName);
if (complexNodes.getSize() > 1 && !currentChildPropertyDefinition.isMultiple())
throw new CmsException("ComplexCmsProperty '"+ currentChildPropertyDefinition.getName() + "' is single value but there are more " +
" than one nodes in "+ propertyContainerNode.getPath());
Map<Integer,CmsProperty<?, ?>> propertiesPerOrder = new TreeMap<Integer, CmsProperty<?,?>>();
int unknownIndex = 10000;
while (complexNodes.hasNext())
{
//Locate node for property
Node nodeOfComplexProperty = complexNodes.nextNode();
//Create new CmsProperty
CmsProperty<?,?> newCmsProperty = createNewCmsProperty(currentChildPropertyDefinition, childPropertyName);
if (newCmsProperty instanceof LazyCmsProperty){
((LazyCmsProperty)newCmsProperty).setPropertyContainerNodeUUID(nodeOfComplexProperty.getIdentifier());
}
//Render Complex Property Id
if (!cmsRepositoryEntityUtils.hasCmsIdentifier(nodeOfComplexProperty)){
throw new CmsException("Found no id for complex property "+ nodeOfComplexProperty.getPath());
}
//Render Id
cmsRepositoryEntityRenderer.renderCmsRepositoryEntityBasicAttributes(nodeOfComplexProperty, newCmsProperty);
if (nodeOfComplexProperty.hasProperty(CmsBuiltInItem.Order.getJcrName()))
{
try{
int index = (int)nodeOfComplexProperty.getProperty(CmsBuiltInItem.Order.getJcrName()).getLong() - 1;
if (propertiesPerOrder.containsKey(index))
{
propertiesPerOrder.put(unknownIndex++,newCmsProperty);
}
else
{
propertiesPerOrder.put(index,newCmsProperty);
}
}
catch(Exception e)
{
logger.warn("Node "+nodeOfComplexProperty.getPath()+" did not have a valid order value and therefore corresponding cms property will be added at the end of the list",
e);
propertiesPerOrder.put(unknownIndex++,newCmsProperty);
}
}
else
{
propertiesPerOrder.put(unknownIndex++,newCmsProperty);
}
}
childCmsProperties.addAll(propertiesPerOrder.values());
}
return childCmsProperties;
}
private void renderVersionNames(SimpleCmsProperty<?,?,?> simpleProperty, Node contentObjectNode, Session session) throws RepositoryException {
VersionHistory versioningHistory = null;
//Render is about an archived content object, therefore node uuid is under jcr:frozenUUID
if (contentObjectNode.hasProperty(JcrBuiltInItem.JcrFrozenUUID.getJcrName()))
versioningHistory = versionUtils.getVersionHistoryForNode(session, cmsRepositoryEntityUtils.getCmsIdentifier(contentObjectNode));
else
versioningHistory = session.getWorkspace().getVersionManager().getVersionHistory(contentObjectNode.getPath());
if (versioningHistory != null)
{
VersionIterator versIter = versioningHistory.getAllVersions();
while (versIter.hasNext())
{
Version currentVersion = versIter.nextVersion();
String versionName = currentVersion.getName();
if (! versionName.equals(JcrBuiltInItem.JcrRootVersion.getJcrName()))
{
//Version with no successors is the base version
Version[] successors = currentVersion.getSuccessors();
//RenderHasVersion
//Current version does not have successors
if (ArrayUtils.isEmpty(successors))
{
if (CmsReadOnlyItem.HasVersion.getJcrName().equals(simpleProperty.getName()))
renderSimpleValue(simpleProperty, versionName,session, null, null);
}
//Render versionName
if (CmsReadOnlyItem.Versions.getJcrName().equals(simpleProperty.getName()))
{
renderSimpleValue(simpleProperty, versionName,session, null, null);
}
}
}
}
}
private void renderSimpleCmsProperty(SimpleCmsProperty<?,?,?> simpleProperty , Property property, Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, Session session, RenderProperties renderProperties) throws RepositoryException {
SimpleCmsPropertyDefinition<?> propertyDefinition = (SimpleCmsPropertyDefinition<?>) simpleProperty.getPropertyDefinition();
//Gather all Value from repository in a list
//regardless if property is multiple or not
List<Value> values = new ArrayList<Value>();
if (propertyDefinition.isMultiple()){
//It may be the case that property used to be single value.
//if so an exception is thrown by JCR.
//we must check in order to avoid it
if (property.getDefinition() != null && ! property.getDefinition().isMultiple()){
values.add(property.getValue());
}
else{
values.addAll(Arrays.asList(property.getValues()));
}
}
else
values.add(property.getValue());
setValuesToSimpleProperty(simpleProperty, propertyDefinition.getValueType(), values, cachedCmsRepositoryEntities, session, renderProperties);
}
private void setValuesToSimpleProperty(SimpleCmsProperty simpleProperty, ValueType definitionValueType, List<Value> values,
Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, Session session, RenderProperties renderProperties) throws RepositoryException {
switch (definitionValueType) {
case Boolean:
for (Value value : values){
simpleProperty.addSimpleTypeValue(value.getBoolean());
}
break;
case String:
for (Value value : values){
try{
simpleProperty.addSimpleTypeValue(value.getString());
}
catch(Exception e){
//Backwards compatibility. There was a bug no check was made
//when entering plain string values. If value is invalid because entered
//value has more characters than maxLegth, provide maxLength characters of this value
//
if (value.getString() != null &&
simpleProperty.getPropertyDefinition() != null &&
((StringPropertyDefinition)simpleProperty.getPropertyDefinition()).getMaxLength() != null &&
((StringPropertyDefinition)simpleProperty.getPropertyDefinition()).getMaxLength() > 0 &&
((StringPropertyDefinition)simpleProperty.getPropertyDefinition()).getMaxLength() < value.getString().length()){
logger.warn("Value "+value.getString()+ " contains more characters "+value.getString().length()+" than allowed " +
((StringPropertyDefinition)simpleProperty.getPropertyDefinition()).getMaxLength()+" . Only the first "+
((StringPropertyDefinition)simpleProperty.getPropertyDefinition()).getMaxLength()+ " characters will be displayed", e);
simpleProperty.addSimpleTypeValue(value.getString().substring(0, ((StringPropertyDefinition)simpleProperty.getPropertyDefinition()).getMaxLength()));
}
else{
logger.warn("Value "+value.getString()+ " is probably invalid. It will not be added to property "+simpleProperty.getFullPath()+
" This value will remain in repository until property is resaved with different value(s). This error may happen if " +
"value constraints have been applied recently to property.", e);
}
}
}
break;
case Date:
for (Value value : values){
try{
simpleProperty.addSimpleTypeValue(value.getDate());
}
catch(Exception e){
logger.warn("Value "+DateUtils.format(value.getDate(), "dd/MM/yyy HH:mm")+ " is probably invalid. It will not be added to property "+simpleProperty.getFullPath()+
" This value will remain in repository until property is resaved with different value(s). This error may happen if " +
"value constraints have been applied recently to property.", e);
}
}
break;
case Double:
for (Value value : values){
try{
simpleProperty.addSimpleTypeValue(value.getDouble());
}
catch(Exception e){
logger.warn("Value "+value.getDouble()+ " is probably invalid. It will not be added to property "+simpleProperty.getFullPath()+
" This value will remain in repository until property is resaved with different value(s). This error may happen if " +
"value constraints have been applied recently to property.", e);
}
}
break;
case Long:
for (Value value : values){
try{
simpleProperty.addSimpleTypeValue(value.getLong());
}
catch(Exception e){
logger.warn("Value "+value.getLong()+ " is probably invalid. It will not be added to property "+simpleProperty.getFullPath()+
" This value will remain in repository until property is resaved with different value(s). This error may happen if " +
"value constraints have been applied recently to property.", e);
}
}
break;
case ObjectReference:
renderContentObject((ObjectReferenceProperty)simpleProperty, values,cachedCmsRepositoryEntities, session, renderProperties);
break;
case TopicReference:
renderTopic(values, session, cachedCmsRepositoryEntities, renderProperties, simpleProperty);
break;
case Binary:
for (Value value : values)
simpleProperty.addSimpleTypeValue(binaryChannelRenderer.renderUnmanagedBinaryChannel(simpleProperty.getName(),
value.getString()));
break;
default:
break;
}
}
private void renderTopic(List<Value> values, Session session, Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, RenderProperties renderProperties, SimpleCmsProperty simpleProperty) throws RepositoryException {
for (Value topicIdAsValue : values)
{
//Do not render Topic if it has already been rendered
Topic topic = null;
String topicIdAsString = topicIdAsValue.getString();
if (cachedCmsRepositoryEntities.containsKey(topicIdAsString))
topic = (Topic) cachedCmsRepositoryEntities.get(topicIdAsString);
else
{
try{
topic = topicRenderer.renderTopic(topicIdAsString,renderProperties, session, cachedCmsRepositoryEntities);
if (!cachedCmsRepositoryEntities.containsKey(topicIdAsString))
cachedCmsRepositoryEntities.put(topicIdAsString, topic);
}
catch(Exception e){
logger.warn("Unable to render topic with id "+topicIdAsString + " for content object property "+ simpleProperty.getFullPath());
}
}
simpleProperty.addSimpleTypeValue(topic);
}
}
private void renderContentObject(ObjectReferenceProperty contentObjectProperty, List<Value> values, Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, Session session, RenderProperties renderProperties) throws RepositoryException {
Map<String, ContentObjectTypeDefinition> cachedContentObjectTypeDefinitions = new HashMap<String, ContentObjectTypeDefinition>();
//Disable Full rendering for ContentObjectReferences
boolean fullRenderIsEnabled = renderProperties != null && renderProperties.allContentObjectPropertiesAreRendered();
if (fullRenderIsEnabled){
renderProperties.renderAllContentObjectProperties(false);
}
for (Value contentObjectIdValue : values){
ContentObject contentObject = null;
String contentObjectIdAsString = contentObjectIdValue.getString();
if (cachedCmsRepositoryEntities.containsKey(contentObjectIdAsString)){
contentObject = (ContentObject)cachedCmsRepositoryEntities.get(contentObjectIdAsString);
}
else{
Node contentObjectNode = cmsRepositoryEntityUtils.retrieveUniqueNodeForContentObject(session, contentObjectIdAsString);
if (contentObjectNode == null){
logger.warn("Content object with id {} does not exist in repository. Value found in property {} and will not be rendered",
contentObjectIdAsString, contentObjectProperty.getFullPath());
}
else{
contentObject = contentObjectRenderer.render(session, contentObjectNode, renderProperties,
cachedContentObjectTypeDefinitions, cachedCmsRepositoryEntities);
cachedCmsRepositoryEntities.put(contentObjectIdAsString, contentObject);
}
}
contentObjectProperty.addSimpleTypeValue(contentObject);
}
if (fullRenderIsEnabled){
renderProperties.renderAllContentObjectProperties(true);
}
}
private void renderSimpleValue(SimpleCmsProperty simpleProperty, Object value, Session session, Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities, RenderProperties renderProperties) throws RepositoryException {
if (value == null)
simpleProperty.addSimpleTypeValue(null);
else
{
final Value jcrValue = JcrValueUtils.getJcrValue(value, simpleProperty.getValueType(), session.getValueFactory());
setValuesToSimpleProperty(simpleProperty, simpleProperty.getValueType(), Arrays.asList(jcrValue), cachedCmsRepositoryEntities, session, renderProperties);
}
}
/*
* Checks to see if there is a value for the provided property path
*/
public boolean valueForPropertyExists(String property,
String jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty, Session session) {
PropertyPath propertyPath = new PropertyPath(property);
if (property == null || propertyPath.getPropertyName() == null){
logger.warn("No property path is provided for parent node UUID {}. Do not know " +
"which child property to check",
jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty);
return false;
}
if (StringUtils.isBlank(jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty)){
//Since no parent exists, no need to check for child
if (logger.isDebugEnabled()){
logger.debug("No id for parent has been provided. No need to check for existence of child property {} ",
propertyPath.getFullPath());
}
return false;
}
if (logger.isDebugEnabled()){
logger.debug("Checking property {} existence", propertyPath.getFullPath());
}
//Load property container node if a UUID is provided
//Otherwise a blank template for this child property will be created
try{
Node propertyContainerNode = session.getNodeByIdentifier(jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty);
return propertyPathContainsValue(propertyPath, propertyContainerNode);
}
catch (Exception e){
logger.warn("Could not locate parent node with UUID "+jcrNodeUUIDWhichCorrespondsToParentComplexCmsProperty+
". Cannot check value existence for property "+ propertyPath.getFullPath(), e);
return false;
}
}
private boolean propertyPathContainsValue(PropertyPath propertyPath,
Node propertyContainerNode) throws PathNotFoundException,
ValueFormatException, RepositoryException {
String propertyName = propertyPath.getPropertyName();
int index = propertyPath.getPropertyIndex();
if (propertyPath.getPropertyDescendantPath() != null){
//This property is a complex one.
//Find it and proceed with the rest of the path
Node nodeRepresentingProperty = findNodeForProperty(propertyContainerNode, propertyName, index);
return propertyPathContainsValue(new PropertyPath(propertyPath.getPropertyDescendantPath()), nodeRepresentingProperty);
}
else{
//Final path part. Property is either a Jcr property or a jcr node
return valueExists(propertyContainerNode, propertyName, index);
}
}
/**
* @param propertyContainerNode
* @param propertyName
* @param index
* @return
* @throws RepositoryException
* @throws ValueFormatException
* @throws PathNotFoundException
*/
private boolean valueExists(Node propertyContainerNode, String propertyName, int index) throws PathNotFoundException, ValueFormatException, RepositoryException {
//Check jcr properties
if (propertyContainerNode!= null && propertyContainerNode.hasProperty(propertyName)){
if (index >-1){
Property property = propertyContainerNode.getProperty(propertyName);
boolean multivalue = property.getDefinition().isMultiple();
int sizeOfValues = 1;
if (multivalue){
sizeOfValues = property.getValues().length;
}
return (index+1) <= sizeOfValues;
}
else {
return true;
}
}
else {
return findNodeForProperty(propertyContainerNode, propertyName, index) != null;
}
}
/**
* @param propertyContainerNode
* @param propertyName
* @param index
* @return
*/
private Node findNodeForProperty(Node parentNode,String propertyName, int index) {
try {
if (parentNode == null || ! parentNode.hasNode(propertyName)){
return null;
}
NodeIterator nodesRepresentingProperty = parentNode.getNodes(propertyName);
if (nodesRepresentingProperty.getSize() == 1){
//Only one node exists
if (index <=0){
//User has not specified index or index is 0 (zero based)
return nodesRepresentingProperty.nextNode();
}
else{
//Index provided by the user does not exist
if (logger.isDebugEnabled()){
logger.debug("Index {} for property {} in parent property {} does not exist", new Object[]{index, propertyName, parentNode.getPath()});
}
return null;
}
}
else{
//Due to the requirement of keeping complex property's index inside a specific jcr property
//we have to search all nodes to find a match for the provided index.
//A negative index is equivalent to 0, that is the first item
if (index <0){
index = 0;
}
boolean atLeastOneNodeFoundWithOrderProperty = false;
Node nodeOfComplexPropertyWhosePositionMatchesIndex = null;
while (nodesRepresentingProperty.hasNext()){
Node nodeOfComplexProperty = nodesRepresentingProperty.nextNode();
long position = nodesRepresentingProperty.getPosition();
if (nodeOfComplexProperty.hasProperty(CmsBuiltInItem.Order.getJcrName())){
atLeastOneNodeFoundWithOrderProperty = true;
int complexPropertyIndex = (int)nodeOfComplexProperty.getProperty(CmsBuiltInItem.Order.getJcrName()).getLong();
if (complexPropertyIndex==index){
return nodeOfComplexProperty;
}
}
if ((int)position == index){
nodeOfComplexPropertyWhosePositionMatchesIndex = nodeOfComplexProperty;
}
}
if (!atLeastOneNodeFoundWithOrderProperty){
return nodeOfComplexPropertyWhosePositionMatchesIndex;
}
else{
if (nodeOfComplexPropertyWhosePositionMatchesIndex != null){
//Some or all of the complex nodes contained Order property but none of them matched index.
//Nevertheless there is a node whose position matched the index (although its Order property either
//does not exist or does not match the index
//Report this as a warning and return value
long indexValueOfNodeOfComplexPropertyWhosePositionMatchesIndex = -1;
if (nodeOfComplexPropertyWhosePositionMatchesIndex.hasProperty(CmsBuiltInItem.Order.getJcrName())){
indexValueOfNodeOfComplexPropertyWhosePositionMatchesIndex = nodeOfComplexPropertyWhosePositionMatchesIndex.getProperty(CmsBuiltInItem.Order.getJcrName()).getLong();
}
PropertyPath requestedPath = new PropertyPath(parentNode.getPath()+"."+propertyName);
requestedPath.setPropertyIndex(index);
logger.warn("Requested node for path {}. Found node {} whose position matched requested index {} but it {}.",
new Object[]{requestedPath.getFullPath(), nodeOfComplexPropertyWhosePositionMatchesIndex, index,
(indexValueOfNodeOfComplexPropertyWhosePositionMatchesIndex == -1 ? " does not contain an 'order' property. However this node will be returned"
: " contains an 'order' property with value "+indexValueOfNodeOfComplexPropertyWhosePositionMatchesIndex+ " instead. No node will be returned" )});
if (indexValueOfNodeOfComplexPropertyWhosePositionMatchesIndex == -1){
return nodeOfComplexPropertyWhosePositionMatchesIndex;
}
}
return null;
}
}
} catch (RepositoryException e) {
logger.error("", e);
return null;
}
}
}