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.web.frontoffice.search.metamodel.impl;
017
018import java.util.Collection;
019import java.util.Map;
020import java.util.stream.Stream;
021
022import org.apache.avalon.framework.service.ServiceException;
023
024import org.ametys.cms.content.ContentHelper;
025import org.ametys.cms.contenttype.ContentType;
026import org.ametys.cms.data.ContentValue;
027import org.ametys.cms.data.type.ModelItemTypeConstants;
028import org.ametys.cms.model.ContentElementDefinition;
029import org.ametys.cms.repository.ContentTypeExpression;
030import org.ametys.plugins.repository.AmetysObjectResolver;
031import org.ametys.plugins.repository.query.expression.Expression;
032import org.ametys.plugins.repository.query.expression.Expression.Operator;
033import org.ametys.plugins.repository.query.expression.OrExpression;
034import org.ametys.runtime.model.ElementDefinition;
035import org.ametys.runtime.model.Enumerator;
036import org.ametys.runtime.model.ModelItem;
037import org.ametys.web.frontoffice.search.metamodel.RestrictedEnumerator;
038
039/**
040 * {@link ReferencingSearchServiceCriterionDefinition} for a {@link ModelItemTypeConstants#CONTENT_ELEMENT_TYPE_ID} attribute.
041 */
042public class ContentReferencingSearchServiceCriterionDefinition extends ReferencingSearchServiceCriterionDefinition<ContentValue> implements ContentElementDefinition
043{
044    /** The resolver */
045    protected AmetysObjectResolver _resolver;
046    /** The content helper */
047    protected ContentHelper _contentHelper;
048    
049    /**
050     * Constructor used to create a FO criterion definition on a referenced item of type content
051     * @param reference the item referenced by this criterion
052     * @param referencePath the path of the criterion's reference
053     * @param baseContentType the content type defining the reference
054     */
055    public ContentReferencingSearchServiceCriterionDefinition(ElementDefinition reference, String referencePath, ContentType baseContentType)
056    {
057        super(reference, referencePath, baseContentType);
058    }
059    
060    @Override
061    public boolean isEnumerated()
062    {
063        return true;
064    }
065    
066    @Override
067    public RestrictedEnumerator<ContentValue> getRestrictedEnumerator(Map<String, Object> contextualParameters)
068    {
069        Enumerator<ContentValue> enumerator = new ContentEnumerator(this, _getContentTypeExtensionPoint(), contextualParameters);
070        return new RestrictedWrappedEnumerator<>(enumerator, getName());
071    }
072    
073    /**
074     * Gets the content type expression for retrieving enumeration of contents
075     * @param parentCTypeId The parent content type id
076     * @return The {@link Expression}
077     */
078    protected Expression getContentTypeExpression(String parentCTypeId)
079    {
080        Stream<String> subCTypesIds = _getContentTypeExtensionPoint().getSubTypes(parentCTypeId).stream();
081        Expression[] exprs = Stream.concat(Stream.of(parentCTypeId), subCTypesIds)
082                .map(cTypeId -> new ContentTypeExpression(Operator.EQ, cTypeId))
083                .toArray(Expression[]::new);
084        return new OrExpression(exprs);
085    }
086    
087
088    /**
089     * Get the order of the content id value for a search criterion definition referencing a model item of type content
090     * @param contentValue the content value
091     * @return the order or null if there is no order
092     */
093    public Long getOrder(ContentValue contentValue)
094    {
095        return contentValue.getContentIfExists()
096                           .filter(_getContentHelper()::isReferenceTable)
097                           .filter(content -> content.hasDefinition("order"))
098                           .filter(content -> content.hasValue("order"))
099                           .map(content -> content.<Long>getValue("order"))
100                           .orElse(null);
101    }
102    
103    @Override
104    public ContentElementDefinition getReference()
105    {
106        return (ContentElementDefinition) super.getReference();
107    }
108    
109    public String getContentTypeId()
110    {
111        return getReference().getContentTypeId();
112    }
113    
114    public void setContentTypeId(String contentTypeId)
115    {
116        throw new UnsupportedOperationException("The criterion definition '" + getName() + "' references a criterion of type content. The content type id can not be set to the criterion definition.");
117    }
118    
119    public Collection< ? extends ModelItem> getModelItems()
120    {
121        return getReference().getModelItems();
122    }
123    
124    /**
125     * Retrieves the {@link AmetysObjectResolver} 
126     * @return the {@link AmetysObjectResolver}
127     */
128    protected AmetysObjectResolver _getAmetysObjectResolver()
129    {
130        if (_resolver == null)
131        {
132            try
133            {
134                _resolver = (AmetysObjectResolver) __serviceManager.lookup(AmetysObjectResolver.ROLE);
135            }
136            catch (ServiceException e)
137            {
138                throw new RuntimeException("Unable to lookup after the ametys object resolver", e);
139            }
140        }
141        
142        return _resolver;
143    }
144    
145    /**
146     * Retrieves the {@link ContentHelper} 
147     * @return the {@link ContentHelper}
148     */
149    protected ContentHelper _getContentHelper()
150    {
151        if (_contentHelper == null)
152        {
153            try
154            {
155                _contentHelper = (ContentHelper) __serviceManager.lookup(ContentHelper.ROLE);
156            }
157            catch (ServiceException e)
158            {
159                throw new RuntimeException("Unable to lookup after the content helper", e);
160            }
161        }
162        
163        return _contentHelper;
164    }
165}
166