/*
* 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.model.impl.query.criteria;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.Taxonomy;
import org.betaconceptframework.astroboa.api.model.Topic;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.model.query.CacheRegion;
import org.betaconceptframework.astroboa.api.model.query.Condition;
import org.betaconceptframework.astroboa.api.model.query.QueryOperator;
import org.betaconceptframework.astroboa.api.model.query.criteria.Criterion;
import org.betaconceptframework.astroboa.api.model.query.criteria.SimpleCriterion;
import org.betaconceptframework.astroboa.api.model.query.criteria.TopicReferenceCriterion;
import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
import org.betaconceptframework.astroboa.model.factory.CriterionFactory;
import org.betaconceptframework.astroboa.model.lazy.LazyLoader;
import org.betaconceptframework.astroboa.util.CmsConstants;
import org.slf4j.LoggerFactory;
/**
* @author Gregory Chomatas (gchomatas@betaconcept.com)
* @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
*
*/
public class TopicReferenceCriterionImpl extends SimpleCriterionImpl implements TopicReferenceCriterion,Serializable{
/**
*
*/
private static final long serialVersionUID = 6034509558499684454L;
private boolean includeSubTopics;
public TopicReferenceCriterionImpl() {
//InternalCondition must be nullified
setInternalCondition(null);
}
@Override
public void propertyIsComplex() {
//Properties of these type are never complex
//Do nothing
}
@Override
public void setCaseMatching(CaseMatching caseMatching) {
//Case matching does not play any role in this context
}
@Override
public void setOperator(QueryOperator operator) {
//Only QueryOperator.EQUALS and QueryOperator.NOT_EQUALS are allowed
if (operator != null && QueryOperator.EQUALS != operator && QueryOperator.NOT_EQUALS != operator
&& QueryOperator.IS_NULL != operator && QueryOperator.IS_NOT_NULL != operator){
throw new CmsException("Invalid query operator "+operator + " for topic reference criterion");
}
super.setOperator(operator);
}
@Override
public void addTopicAsAValue(Topic topicReference) {
if (topicReference != null){
if (StringUtils.isNotBlank(topicReference.getId())){
addValue(topicReference.getId());
}
else if (StringUtils.isNotBlank(topicReference.getName())){
addValue(CmsConstants.TOPIC_REFERENCE_CRITERION_VALUE_PREFIX+topicReference.getName());
}
else{
LoggerFactory.getLogger(getClass()).warn("Topic {} has neither an identifier nor a name and therefore cannot be used as a criterion value ", topicReference.toString());
}
}
else{
LoggerFactory.getLogger(getClass()).warn("Topic is null and therefore cannot be used as a criterion value ");
}
}
@Override
public void addTopicsAsValues(List<Topic> topicReferences) {
if (CollectionUtils.isNotEmpty(topicReferences)){
for (Topic topicReference: topicReferences){
addTopicAsAValue(topicReference);
}
}
}
@Override
public String getXPath() {
LazyLoader lazyLoaderForActiveClient = AstroboaClientContextHolder.getLazyLoaderForActiveClient();
String activeAuthenticationToken = AstroboaClientContextHolder.getActiveAuthenticationToken();
Map<String, List<String>> topicPropertyPathsPerTaxonomies = null;
if (lazyLoaderForActiveClient != null){
topicPropertyPathsPerTaxonomies = lazyLoaderForActiveClient.getTopicPropertyPathsPerTaxonomies(activeAuthenticationToken);
}
Condition internalCondition = getInternalCondition();
if (internalCondition == null){
internalCondition = Condition.OR;
}
Criterion criterion = null;
if (CollectionUtils.isEmpty(getValues())){
Set<String> possiblePropertyPaths = loadTopicPropertyPaths(null, topicPropertyPathsPerTaxonomies);
for (String topicPropertyPath : possiblePropertyPaths){
criterion = createCriterionForProperty(internalCondition, topicPropertyPath, criterion, null);
}
}
else{
for (Object value : getValues()){
List parentAndChildIds = new ArrayList();
String taxonomyName = null;
String topicId = checkIfValueIsAReferenceAndLoadReferenceId((String)value);
if (topicId != null){
parentAndChildIds.add(topicId);
if (lazyLoaderForActiveClient != null){
if (includeSubTopics){
lazyLoaderForActiveClient.loadChildTopicIdsForSpecifiedProfileSubjectIds(parentAndChildIds, activeAuthenticationToken, CacheRegion.NONE);
}
try{
Taxonomy taxonomy = lazyLoaderForActiveClient.lazyLoadTaxonomyForTopic(topicId, activeAuthenticationToken);
if (taxonomy != null){
taxonomyName = taxonomy.getName();
}
}
catch(Exception e){
e.printStackTrace();
}
}
}
Set<String> possiblePropertyPaths = loadTopicPropertyPaths(taxonomyName, topicPropertyPathsPerTaxonomies);
for (String topicPropertyPath : possiblePropertyPaths){
criterion = createCriterionForProperty(internalCondition, topicPropertyPath, criterion, parentAndChildIds);
}
}
}
if (criterion == null){
return "";
}
else{
return criterion.getXPath();
}
}
private Criterion createCriterionForProperty(Condition internalCondition,
String topicPropertyPath, Criterion topicCriterion, List parentAndChildIds) {
Criterion parentAndChildCriterion = createSimpleCriterionForItem(getOperator(), parentAndChildIds, Condition.OR, topicPropertyPath);
if (topicCriterion == null){
topicCriterion = parentAndChildCriterion;
}
else{
switch (internalCondition) {
case AND:
topicCriterion = CriterionFactory.and(topicCriterion, parentAndChildCriterion);
break;
case OR:
topicCriterion = CriterionFactory.or(topicCriterion, parentAndChildCriterion);
break;
default:
break;
}
}
return topicCriterion;
}
private Set<String> loadTopicPropertyPaths(String taxonomyName, Map<String, List<String>> topicPropertyPathsPerTaxonomies){
Set<String> possiblePropertyPaths = new HashSet<String>();
if (getProperty() != null){
possiblePropertyPaths.add(getProperty());
}
else{
if (MapUtils.isNotEmpty(topicPropertyPathsPerTaxonomies)){
//Build one criterion for each topic property that belongs to the specified taxonomy
//If no taxonomy is provided, all topic properties will be included
if (StringUtils.isBlank(taxonomyName)){
for (List<String> topicProperyPaths : topicPropertyPathsPerTaxonomies.values()){
if (CollectionUtils.isNotEmpty(topicProperyPaths)){
for (String topicPropertyPath: topicProperyPaths){
possiblePropertyPaths.add(topicPropertyPath);
}
}
}
}
else{
//Get all topicProperties for provided taxonomy
List<String> topicPropertyPathsForTaxonomy = topicPropertyPathsPerTaxonomies.get(taxonomyName);
if (CollectionUtils.isNotEmpty(topicPropertyPathsForTaxonomy)){
for (String topicPropertyPath: topicPropertyPathsForTaxonomy){
possiblePropertyPaths.add(topicPropertyPath);
}
}
//Finally add all topic properties that are not connected to any taxonomy
List<String> topicPropertyPathsWithoutTaxonomy = topicPropertyPathsPerTaxonomies.get(CmsConstants.ANY_TAXONOMY);
if (CollectionUtils.isNotEmpty(topicPropertyPathsWithoutTaxonomy)){
for (String topicPropertyPath: topicPropertyPathsWithoutTaxonomy){
possiblePropertyPaths.add(topicPropertyPath);
}
}
}
}
}
return possiblePropertyPaths;
}
private SimpleCriterion createSimpleCriterionForItem(QueryOperator queryOperator, List values, Condition internalCondition, String property) {
SimpleCriterion criterion = CriterionFactory.newSimpleCriterion();
criterion.setValues(values);
criterion.setInternalCondition(internalCondition);
criterion.setProperty(property);
criterion.setOperator(queryOperator);
return criterion;
}
@Override
public void expandCriterionToIncludeSubTopics() {
includeSubTopics = true;
}
@Override
public void addValue(Object value) {
if (value != null){
if (value instanceof Topic){
addTopicAsAValue((Topic)value);
}
else{
if (value instanceof String && ((String) value).endsWith(CmsConstants.INCLUDE_CHILDREN_EXPRESSION)){
expandCriterionToIncludeSubTopics();
super.addValue(StringUtils.substringBeforeLast((String)value, CmsConstants.INCLUDE_CHILDREN_EXPRESSION));
}
else{
super.addValue(value);
}
}
}
}
@Override
public void setValues(List<Object> values) {
if (values != null){
for (Object value : values){
addValue(value);
}
}
}
}