/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.content;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.joda.time.DateTime;
import com.enonic.cms.core.content.resultset.ContentResultSet;
import com.enonic.cms.core.content.resultset.ContentResultSetNonLazy;
import com.enonic.cms.core.content.resultset.RelatedChildContent;
import com.enonic.cms.core.content.resultset.RelatedContent;
import com.enonic.cms.core.content.resultset.RelatedContentResultSet;
import com.enonic.cms.core.content.resultset.RelatedContentResultSetImpl;
import com.enonic.cms.core.content.resultset.RelatedParentContent;
import com.enonic.cms.core.portal.livetrace.RelatedContentFetchTrace;
import com.enonic.cms.core.portal.livetrace.RelatedContentFetchTracer;
import com.enonic.cms.store.dao.ContentDao;
import com.enonic.cms.store.dao.RelatedParentContentQuery;
public class RelatedContentFetcher
extends AbstractRelatedContentFetcher
{
private Integer maxParentLevel;
private Integer maxParentChildrenLevel;
private boolean includeOnlyMainVersions = true;
/**
* The content which this fetcher should find the related content of.
*/
private ContentResultSet contentResultSet;
public RelatedContentFetcher( ContentDao contentDao, final RelatedContentFetchTrace trace )
{
super( contentDao, trace );
}
public RelatedContentFetcher( ContentDao contentDao )
{
super( contentDao, null );
}
public RelatedContentResultSet fetch( final ContentEntity content )
{
this.contentResultSet = new ContentResultSetNonLazy( content );
return doFetch( false );
}
/**
* Retrieves both parent and children of the given content, depending on the parentLevel and childrenLevel configuration of this class.
*
* @param content The initial content to get related content for.
* @param includeVisited If <code>true</code> all content will be retrieved. If <code>false</code>, content that have already been
* fetched with this fetcher in a previous call, will not be included in the result set
* @return A complete result set of related content
*/
public RelatedContentResultSet fetch( final ContentEntity content, boolean includeVisited )
{
this.contentResultSet = new ContentResultSetNonLazy( content );
return doFetch( includeVisited );
}
public RelatedContentResultSet fetch( final ContentResultSet contentResultSet )
{
this.contentResultSet = contentResultSet;
return doFetch( false );
}
private RelatedContentResultSet doFetch( boolean includeVisited )
{
relatedContentResultSet = new RelatedContentResultSetImpl();
boolean fetchChildren = maxChildrenLevel > 0;
if ( fetchChildren )
{
Collection<RelatedChildContent> rootRelatedChildren =
doFindRelatedChildren( gatherMainVersionsFromContent( contentResultSet.getContents() ) );
RelatedContentFetchTracer.traceChildrenFetch( 1, rootRelatedChildren.size(), trace );
if ( rootRelatedChildren.size() > 0 )
{
doAddAndFetchChildren( rootRelatedChildren, maxChildrenLevel, includeVisited );
for ( RelatedChildContent rootRelatedChild : rootRelatedChildren )
{
relatedContentResultSet.addRootRelatedChild( rootRelatedChild );
}
}
}
boolean fetchParents = maxParentLevel > 0;
if ( fetchParents )
{
Collection<RelatedParentContent> rootRelatedParents = doFindRelatedParents( contentResultSet.getKeys() );
RelatedContentFetchTracer.traceParentsFetch( 1, rootRelatedParents.size(), trace );
if ( rootRelatedParents.size() > 0 )
{
doAddAndFetchParents( rootRelatedParents, maxParentLevel, includeVisited );
for ( RelatedParentContent rootRelatedParent : rootRelatedParents )
{
relatedContentResultSet.addRootRelatedParent( rootRelatedParent );
}
}
}
return relatedContentResultSet;
}
private List<RelatedParentContent> doAddAndFetchParents( final Collection<RelatedParentContent> parentsToAdd, final int level,
final boolean includeVisited )
{
final int nextLevel = level - 1;
List<RelatedParentContent> addedRelatedContent = new ArrayList<RelatedParentContent>();
final List<ContentEntity> addedContent = new ArrayList<ContentEntity>();
for ( RelatedParentContent relatedToAdd : parentsToAdd )
{
if ( isAddable( relatedToAdd, includeVisited ) )
{
addedRelatedContent.add( relatedToAdd );
addedContent.add( relatedToAdd.getContent() );
relatedContentResultSet.add( relatedToAdd );
registerForFastAccess( relatedToAdd );
doConnectRelatedContent( relatedToAdd );
}
visitedParentRelatedContent.add( relatedToAdd.getContent().getKey() );
}
// fetch parent children...
final boolean fetchParentChildren = level == maxParentLevel && maxParentChildrenLevel > 0;
if ( fetchParentChildren )
{
final Collection<RelatedChildContent> nextLevelChildren =
doFindRelatedChildren( gatherMainVersionsFromContent( addedContent ) );
if ( nextLevelChildren.size() > 0 )
{
doAddAndFetchChildren( nextLevelChildren, maxParentChildrenLevel, includeVisited );
}
}
// fetch more parents...
final boolean atLastLevel = nextLevel == 0;
if ( !atLastLevel )
{
final Collection<RelatedParentContent> nextLevelParents = doFindRelatedParents( gatherContentKeysFromContent( addedContent ) );
RelatedContentFetchTracer.traceParentsFetch( level, nextLevelParents.size(), trace );
if ( nextLevelParents.size() > 0 )
{
doAddAndFetchParents( nextLevelParents, nextLevel, includeVisited );
}
}
return addedRelatedContent;
}
@Override
protected boolean isAddable( final RelatedContent relatedToAdd, boolean includeVisited )
{
final boolean contentIsAllreadyVisited;
if ( relatedToAdd instanceof RelatedParentContent )
{
contentIsAllreadyVisited = visitedParentRelatedContent.contains( relatedToAdd.getContent().getKey() );
}
else
{
contentIsAllreadyVisited = visitedChildRelatedContent.contains( relatedToAdd.getContent().getKey() );
}
return includeVisited || !contentIsAllreadyVisited;
}
private Collection<RelatedParentContent> doFindRelatedParents( List<ContentKey> contentKeys )
{
if ( contentKeys.size() == 0 )
{
return new ArrayList<RelatedParentContent>();
}
final RelatedParentContentQuery relatedParentContentQuery = new RelatedParentContentQuery();
relatedParentContentQuery.contents( contentKeys );
relatedParentContentQuery.includeOnlyMainVersions( includeOnlyMainVersions );
relatedParentContentQuery.now( new DateTime( availableCheckDate ) );
relatedParentContentQuery.includeOfflineContent( includeOfflineContent() );
relatedParentContentQuery.securityFilter( securityFilter );
return contentDao.findRelatedParentByKeys( relatedParentContentQuery );
}
public void setMaxParentLevel( Integer value )
{
this.maxParentLevel = value;
}
public void setMaxParentChildrenLevel( Integer value )
{
this.maxParentChildrenLevel = value;
}
public void setIncludeOnlyMainVersions( boolean includeOnlyMainVersions )
{
this.includeOnlyMainVersions = includeOnlyMainVersions;
}
public Integer getMaxParentLevel()
{
return maxParentLevel;
}
}