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.plugins.odfweb.service.search;
017
018import java.util.Arrays;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.Optional;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025
026import org.ametys.cms.search.query.AndQuery;
027import org.ametys.cms.search.query.ConstantNilScoreQuery;
028import org.ametys.cms.search.query.ContentTypeQuery;
029import org.ametys.cms.search.query.IsolateQuery;
030import org.ametys.cms.search.query.JoinQuery;
031import org.ametys.cms.search.query.MaxScoreOrQuery;
032import org.ametys.cms.search.query.Query;
033import org.ametys.odf.program.Program;
034import org.ametys.odf.program.ProgramFactory;
035import org.ametys.odf.program.SubProgramFactory;
036import org.ametys.odf.program.TraversableProgramPart;
037import org.ametys.plugins.odfweb.service.search.criterion.DegreeUniversityAttributeContentSearchCriterionDefinition;
038import org.ametys.plugins.odfweb.service.search.helper.DegreeUniversityHelper;
039import org.ametys.web.frontoffice.search.metamodel.AdditionalParameterValueMap;
040import org.ametys.web.frontoffice.search.metamodel.Returnable;
041import org.ametys.web.frontoffice.search.metamodel.SearchCriterionDefinition;
042import org.ametys.web.frontoffice.search.metamodel.Searchable;
043import org.ametys.web.frontoffice.search.metamodel.impl.AbstractContentBasedSearchable;
044
045/**
046 * {@link Searchable} for {@link Program}s
047 */
048public class ProgramSearchable extends AbstractContentBasedSearchable
049{
050    /** Avalon Role */
051    public static final String ROLE = ProgramSearchable.class.getName();
052    
053    /** The additional parameter for indicating if search has to be made also on subprograms */
054    public static final String PARAMETER_SEARCH_ON_SUBPROGRAMS = "searchSubprogram";
055    
056    /** The prefix for program searchable */
057    public static final String CRITERION_DEFINITIONS_PREFIX_ID = "ProgramSearchable$";
058
059    /** The prefix for indexing field in program searcheable */
060    public static final String PROGRAM_SEARCHEABLE_INDEXING_FIELD_PREFIX = CRITERION_DEFINITIONS_PREFIX_ID + "indexingField$" + ProgramFactory.PROGRAM_CONTENT_TYPE + "$";
061    
062    /** The ODF search helper */
063    protected ODFSearchHelper _odfSearchHelper;
064    
065    /** The degree university helper */
066    protected DegreeUniversityHelper _degreeUniversityHelper;
067    
068    @Override
069    public void service(ServiceManager manager) throws ServiceException
070    {
071        super.service(manager);
072        _odfSearchHelper = (ODFSearchHelper) manager.lookup(ODFSearchHelper.ROLE);
073        _degreeUniversityHelper = (DegreeUniversityHelper) manager.lookup(DegreeUniversityHelper.ROLE);
074    }
075    
076    @Override
077    protected String associatedContentReturnableRole()
078    {
079        return ProgramReturnable.ROLE;
080    }
081    
082    @Override
083    protected String getCriterionDefinitionPrefix()
084    {
085        return CRITERION_DEFINITIONS_PREFIX_ID;
086    }
087    
088    @Override
089    public Collection<Returnable> relationsWith()
090    {
091        return Arrays.asList(_associatedContentReturnable);
092    }
093    
094    @Override
095    protected Collection<String> getContentTypes(AdditionalParameterValueMap additionalParameterValues)
096    {
097        return Collections.singleton(ProgramFactory.PROGRAM_CONTENT_TYPE);
098    }
099    
100    @Override
101    public Optional<Query> joinQuery(Query queryOnCriterion, SearchCriterionDefinition criterion, Collection<Returnable> returnables, AdditionalParameterValueMap additionalParameters)
102    {
103        if (returnables.contains(_associatedContentReturnable))
104        {
105            if (_searchOnSubprograms(additionalParameters) && appliesToSubprogram(criterion))
106            {
107                return Optional.of(new MaxScoreOrQuery(new IsolateQuery(queryOnCriterion), getProgramThroughSubprogramsQuery(queryOnCriterion)));
108            }
109            else
110            {
111                return Optional.of(queryOnCriterion);
112            }
113        }
114        else
115        {
116            return Optional.empty();
117        }
118    }
119    
120    private boolean _searchOnSubprograms(AdditionalParameterValueMap additionalParameters)
121    {
122        return additionalParameters.getValue(PARAMETER_SEARCH_ON_SUBPROGRAMS);
123    }
124    
125    /**
126     * Determines if the given {@link SearchCriterionDefinition} applies to Subprograms
127     * @param criterion the criterion
128     * @return <code>true</code> if the given {@link SearchCriterionDefinition} applies to Subprograms
129     */
130    protected boolean appliesToSubprogram(SearchCriterionDefinition criterion)
131    {
132        return _odfSearchHelper.isCriterionOnBothProgramAndSubProgram(criterion);
133    }
134    
135    /**
136     * Gets the {@link Query} that returns Programs by querying their SubPrograms
137     * @param queryOnCriterion The query on SubPrograms
138     * @return The {@link Query} that returns Programs
139     */
140    protected Query getProgramThroughSubprogramsQuery(Query queryOnCriterion)
141    {
142        Query queryOnSubprograms = new AndQuery(
143            new ConstantNilScoreQuery(new ContentTypeQuery(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE)),
144            queryOnCriterion
145        );
146        return new ProgramThroughProgramPartsQuery(queryOnSubprograms);
147    }
148    
149    @Override
150    public Collection<SearchCriterionDefinition> getCriteria(AdditionalParameterValueMap additionalParameterValues)
151    {
152        Collection<SearchCriterionDefinition> criteria = super.getCriteria(additionalParameterValues);
153        
154        DegreeUniversityAttributeContentSearchCriterionDefinition degreeUniversityCriterionDefinition = _degreeUniversityHelper.getDegreeUniversityCriterionDefinition(this);
155        if (degreeUniversityCriterionDefinition != null)
156        {
157            criteria.add(degreeUniversityCriterionDefinition);
158        }
159        
160        return criteria;
161    }
162
163    /**
164     * A {@link Query} that returns Programs, by querying their child ProgramParts
165     */
166    protected static class ProgramThroughProgramPartsQuery extends JoinQuery
167    {
168        /**
169         * Builds a ProgramThroughProgramPartsQuery
170         * @param subQuery The query on ProgramParts
171         */
172        protected ProgramThroughProgramPartsQuery(Query subQuery)
173        {
174            super(subQuery, TraversableProgramPart.CHILD_PROGRAM_PARTS);
175        }
176    }
177}