001/*
002 * (C) Copyright 2010-2017 Nuxeo (http://nuxeo.com/) and others.
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 *
016 * Contributors:
017 *     Anahide Tchertchian
018 */
019package org.nuxeo.ecm.platform.query.core;
020
021import java.io.Serializable;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027
028import org.nuxeo.ecm.core.api.DocumentModel;
029import org.nuxeo.ecm.core.api.NuxeoException;
030import org.nuxeo.ecm.core.api.SortInfo;
031import org.nuxeo.ecm.platform.query.api.PageProvider;
032import org.nuxeo.ecm.platform.query.api.PageProviderClassReplacerDefinition;
033import org.nuxeo.ecm.platform.query.api.PageProviderDefinition;
034import org.nuxeo.ecm.platform.query.api.PageProviderService;
035import org.nuxeo.ecm.platform.query.api.QuickFilter;
036import org.nuxeo.ecm.platform.query.nxql.CoreQueryDocumentPageProvider;
037import org.nuxeo.runtime.model.ComponentContext;
038import org.nuxeo.runtime.model.ComponentInstance;
039import org.nuxeo.runtime.model.DefaultComponent;
040
041/**
042 * @author Anahide Tchertchian
043 * @since 5.4
044 */
045public class PageProviderServiceImpl extends DefaultComponent implements PageProviderService {
046
047    private static final long serialVersionUID = 1L;
048
049    public static final String PROVIDER_EP = "providers";
050
051    // @since 6.0
052    public static final String REPLACER_EP = "replacers";
053
054    /**
055     * @deprecated since 6.0, use {@link PageProviderService#NAMED_PARAMETERS} instead.
056     */
057    @Deprecated
058    public static final String NAMED_PARAMETERS = "namedParameters";
059
060    protected PageProviderRegistry providerReg = new PageProviderRegistry();
061
062    // @since 6.0
063    protected PageProviderClassReplacerRegistry replacersReg = new PageProviderClassReplacerRegistry();
064
065    @Override
066    public PageProviderDefinition getPageProviderDefinition(String name) {
067        PageProviderDefinition def = providerReg.getPageProvider(name);
068        if (def == null) {
069            return null;
070        }
071        return def.clone();
072    }
073
074    @Override
075    public PageProvider<?> getPageProvider(String name, PageProviderDefinition desc, DocumentModel searchDocument,
076            List<SortInfo> sortInfos, Long pageSize, Long currentPage, Map<String, Serializable> properties,
077            List<String> highlights, List<QuickFilter> quickFilters, Object... parameters) {
078        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, null, properties,
079                highlights, quickFilters, parameters);
080    }
081
082    @Override
083    public PageProvider<?> getPageProvider(String name, PageProviderDefinition desc, DocumentModel searchDocument,
084            List<SortInfo> sortInfos, Long pageSize, Long currentPage, Long currentOffset,
085            Map<String, Serializable> properties, List<String> highlights, List<QuickFilter> quickFilters,
086            Object... parameters) {
087
088        if (desc == null) {
089            return null;
090        }
091        PageProvider<?> pageProvider = newPageProviderInstance(name, desc);
092        // XXX: set local properties without resolving, and merge with given
093        // properties.
094        Map<String, Serializable> allProps = new HashMap<>();
095        Map<String, String> localProps = desc.getProperties();
096        if (localProps != null) {
097            allProps.putAll(localProps);
098        }
099        if (properties != null) {
100            allProps.putAll(properties);
101        }
102        pageProvider.setProperties(allProps);
103        pageProvider.setSortable(desc.isSortable());
104        pageProvider.setParameters(parameters);
105        pageProvider.setPageSizeOptions(desc.getPageSizeOptions());
106        if (searchDocument != null) {
107            pageProvider.setSearchDocumentModel(searchDocument);
108        }
109
110        Long maxPageSize = desc.getMaxPageSize();
111        if (maxPageSize != null) {
112            pageProvider.setMaxPageSize(maxPageSize.longValue());
113        }
114
115        if (sortInfos == null) {
116            pageProvider.setSortInfos(desc.getSortInfos());
117        } else {
118            pageProvider.setSortInfos(sortInfos);
119        }
120
121        if (quickFilters != null) {
122            pageProvider.setQuickFilters(quickFilters);
123        }
124
125        if (highlights != null) {
126            pageProvider.setHighlights(highlights);
127        }
128
129        if (pageSize == null || pageSize.longValue() < 0) {
130            pageProvider.setPageSize(desc.getPageSize());
131        } else {
132            pageProvider.setPageSize(pageSize.longValue());
133        }
134        if (currentPage != null && currentPage.longValue() > 0) {
135            pageProvider.setCurrentPage(currentPage.longValue());
136        }
137        if (currentOffset != null && currentOffset.longValue() >= 0) {
138            pageProvider.setCurrentPageOffset(currentOffset.longValue());
139        }
140
141        return pageProvider;
142    }
143
144    @Override
145    public PageProvider<?> getPageProvider(String name, PageProviderDefinition desc, DocumentModel searchDocument,
146            List<SortInfo> sortInfos, Long pageSize, Long currentPage, Map<String, Serializable> properties,
147            List<QuickFilter> quickFilters, Object... parameters) {
148        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, null,
149                quickFilters, parameters);
150    }
151
152    @Override
153    public PageProvider<?> getPageProvider(String name, List<SortInfo> sortInfos, Long pageSize, Long currentPage,
154            Map<String, Serializable> properties, List<String> highlights, List<QuickFilter> quickFilters,
155            Object... parameters) {
156        return getPageProvider(name, (DocumentModel) null, sortInfos, pageSize, currentPage, properties, highlights,
157                quickFilters, parameters);
158    }
159
160    @Override
161    public PageProvider<?> getPageProvider(String name, PageProviderDefinition desc, DocumentModel searchDocument,
162            List<SortInfo> sortInfos, Long pageSize, Long currentPage, Map<String, Serializable> properties,
163            Object... parameters) {
164
165        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, null, null,
166                parameters);
167    }
168
169    protected PageProvider<?> newPageProviderInstance(String name, PageProviderDefinition desc) {
170        PageProvider<?> ret;
171        if (desc instanceof CoreQueryPageProviderDescriptor) {
172            ret = newCoreQueryPageProviderInstance(name);
173        } else if (desc instanceof GenericPageProviderDescriptor) {
174            Class<PageProvider<?>> klass = ((GenericPageProviderDescriptor) desc).getPageProviderClass();
175            ret = newPageProviderInstance(name, klass);
176        } else {
177            throw new NuxeoException(String.format("Invalid page provider definition with name '%s'", name));
178        }
179        ret.setName(name);
180        ret.setDefinition(desc);
181        return ret;
182    }
183
184    protected PageProvider<?> newCoreQueryPageProviderInstance(String name) {
185        PageProvider<?> ret;
186        Class<? extends PageProvider<?>> klass = replacersReg.getClassForPageProvider(name);
187        if (klass == null) {
188            ret = new CoreQueryDocumentPageProvider();
189        } else {
190            ret = newPageProviderInstance(name, klass);
191        }
192        return ret;
193    }
194
195    protected PageProvider<?> newPageProviderInstance(String name, Class<? extends PageProvider<?>> klass) {
196        PageProvider<?> ret;
197        if (klass == null) {
198            throw new NuxeoException(String.format(
199                    "Cannot find class for page provider definition with name '%s': check" + " ERROR logs at startup",
200                    name));
201        }
202        try {
203            ret = klass.newInstance();
204        } catch (ReflectiveOperationException e) {
205            throw new NuxeoException(String.format(
206                    "Cannot create an instance of class %s for page provider definition" + " with name '%s'",
207                    klass.getName(), name), e);
208        }
209        return ret;
210    }
211
212    @Override
213    public PageProvider<?> getPageProvider(String name, DocumentModel searchDocument, List<SortInfo> sortInfos,
214            Long pageSize, Long currentPage, Map<String, Serializable> properties, Object... parameters) {
215        PageProviderDefinition desc = providerReg.getPageProvider(name);
216        if (desc == null) {
217            throw new NuxeoException(String.format("Could not resolve page provider with name '%s'", name));
218        }
219        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, null, null,
220                parameters);
221    }
222
223    @Override
224    public PageProvider<?> getPageProvider(String name, DocumentModel searchDocument, List<SortInfo> sortInfos,
225            Long pageSize, Long currentPage, Map<String, Serializable> properties, List<String> highlights,
226            List<QuickFilter> quickFilters, Object... parameters) {
227        PageProviderDefinition desc = providerReg.getPageProvider(name);
228        if (desc == null) {
229            throw new NuxeoException(String.format("Could not resolve page provider with name '%s'", name));
230        }
231        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, highlights,
232                quickFilters, parameters);
233    }
234
235    @Override
236    public PageProvider<?> getPageProvider(String name, DocumentModel searchDocument, List<SortInfo> sortInfos,
237            Long pageSize, Long currentPage, Long currentOffset, Map<String, Serializable> properties,
238            List<String> highlights, List<QuickFilter> quickFilters, Object... parameters) {
239        PageProviderDefinition desc = providerReg.getPageProvider(name);
240        if (desc == null) {
241            throw new NuxeoException(String.format("Could not resolve page provider with name '%s'", name));
242        }
243        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, currentOffset, properties,
244                highlights, quickFilters, parameters);
245    }
246
247    @Override
248    public PageProvider<?> getPageProvider(String name, DocumentModel searchDocument, List<SortInfo> sortInfos,
249            Long pageSize, Long currentPage, Map<String, Serializable> properties, List<QuickFilter> quickFilters,
250            Object... parameters) {
251        PageProviderDefinition desc = providerReg.getPageProvider(name);
252        if (desc == null) {
253            throw new NuxeoException(String.format("Could not resolve page provider with name '%s'", name));
254        }
255        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, quickFilters,
256                parameters);
257    }
258
259    @Override
260    public PageProvider<?> getPageProvider(String name, List<SortInfo> sortInfos, Long pageSize, Long currentPage,
261            Map<String, Serializable> properties, Object... parameters) {
262        return getPageProvider(name, (DocumentModel) null, sortInfos, pageSize, currentPage, properties, parameters);
263    }
264
265    @Override
266    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
267        if (PROVIDER_EP.equals(extensionPoint)) {
268            PageProviderDefinition desc = (PageProviderDefinition) contribution;
269            registerPageProviderDefinition(desc);
270        } else if (REPLACER_EP.equals(extensionPoint)) {
271            PageProviderClassReplacerDefinition desc = (PageProviderClassReplacerDefinition) contribution;
272            replacersReg.addContribution(desc);
273        }
274    }
275
276    @Override
277    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
278        if (PROVIDER_EP.equals(extensionPoint)) {
279            PageProviderDefinition desc = (PageProviderDefinition) contribution;
280            unregisterPageProviderDefinition(desc);
281        }
282    }
283
284    @Override
285    public void start(ComponentContext context) {
286        replacersReg.dumpReplacerMap();
287    }
288
289    @Override
290    public void registerPageProviderDefinition(PageProviderDefinition desc) {
291        providerReg.addContribution(desc);
292    }
293
294    @Override
295    public void unregisterPageProviderDefinition(PageProviderDefinition desc) {
296        providerReg.removeContribution(desc);
297    }
298
299    @Override
300    public Set<String> getPageProviderDefinitionNames() {
301        return Collections.unmodifiableSet(providerReg.providers.keySet());
302    }
303
304}