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
079        if (desc == null) {
080            return null;
081        }
082        PageProvider<?> pageProvider = newPageProviderInstance(name, desc);
083        // XXX: set local properties without resolving, and merge with given
084        // properties.
085        Map<String, Serializable> allProps = new HashMap<>();
086        Map<String, String> localProps = desc.getProperties();
087        if (localProps != null) {
088            allProps.putAll(localProps);
089        }
090        if (properties != null) {
091            allProps.putAll(properties);
092        }
093        pageProvider.setProperties(allProps);
094        pageProvider.setSortable(desc.isSortable());
095        pageProvider.setParameters(parameters);
096        pageProvider.setPageSizeOptions(desc.getPageSizeOptions());
097        if (searchDocument != null) {
098            pageProvider.setSearchDocumentModel(searchDocument);
099        }
100
101        Long maxPageSize = desc.getMaxPageSize();
102        if (maxPageSize != null) {
103            pageProvider.setMaxPageSize(maxPageSize.longValue());
104        }
105
106        if (sortInfos == null) {
107            pageProvider.setSortInfos(desc.getSortInfos());
108        } else {
109            pageProvider.setSortInfos(sortInfos);
110        }
111
112        if (quickFilters != null) {
113            pageProvider.setQuickFilters(quickFilters);
114        }
115
116        if (highlights != null) {
117            pageProvider.setHighlights(highlights);
118        }
119
120        if (pageSize == null || pageSize.longValue() < 0) {
121            pageProvider.setPageSize(desc.getPageSize());
122        } else {
123            pageProvider.setPageSize(pageSize.longValue());
124        }
125        if (currentPage != null && currentPage.longValue() > 0) {
126            pageProvider.setCurrentPage(currentPage.longValue());
127        }
128
129        return pageProvider;
130    }
131
132    @Override
133    public PageProvider<?> getPageProvider(String name, PageProviderDefinition desc, DocumentModel searchDocument,
134            List<SortInfo> sortInfos, Long pageSize, Long currentPage, Map<String, Serializable> properties,
135            List<QuickFilter> quickFilters, Object... parameters) {
136        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, null,
137                quickFilters, parameters);
138    }
139
140    @Override
141    public PageProvider<?> getPageProvider(String name, List<SortInfo> sortInfos, Long pageSize, Long currentPage,
142            Map<String, Serializable> properties, List<String> highlights, List<QuickFilter> quickFilters,
143            Object... parameters) {
144        return getPageProvider(name, (DocumentModel) null, sortInfos, pageSize, currentPage, properties, highlights,
145                quickFilters, parameters);
146    }
147
148    @Override
149    public PageProvider<?> getPageProvider(String name, PageProviderDefinition desc, DocumentModel searchDocument,
150            List<SortInfo> sortInfos, Long pageSize, Long currentPage, Map<String, Serializable> properties,
151            Object... parameters) {
152
153        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, null, null,
154                parameters);
155    }
156
157    protected PageProvider<?> newPageProviderInstance(String name, PageProviderDefinition desc) {
158        PageProvider<?> ret;
159        if (desc instanceof CoreQueryPageProviderDescriptor) {
160            ret = newCoreQueryPageProviderInstance(name);
161        } else if (desc instanceof GenericPageProviderDescriptor) {
162            Class<PageProvider<?>> klass = ((GenericPageProviderDescriptor) desc).getPageProviderClass();
163            ret = newPageProviderInstance(name, klass);
164        } else {
165            throw new NuxeoException(String.format("Invalid page provider definition with name '%s'", name));
166        }
167        ret.setName(name);
168        ret.setDefinition(desc);
169        return ret;
170    }
171
172    protected PageProvider<?> newCoreQueryPageProviderInstance(String name) {
173        PageProvider<?> ret;
174        Class<? extends PageProvider<?>> klass = replacersReg.getClassForPageProvider(name);
175        if (klass == null) {
176            ret = new CoreQueryDocumentPageProvider();
177        } else {
178            ret = newPageProviderInstance(name, klass);
179        }
180        return ret;
181    }
182
183    protected PageProvider<?> newPageProviderInstance(String name, Class<? extends PageProvider<?>> klass) {
184        PageProvider<?> ret;
185        if (klass == null) {
186            throw new NuxeoException(String.format(
187                    "Cannot find class for page provider definition with name '%s': check" + " ERROR logs at startup",
188                    name));
189        }
190        try {
191            ret = klass.newInstance();
192        } catch (ReflectiveOperationException e) {
193            throw new NuxeoException(String.format(
194                    "Cannot create an instance of class %s for page provider definition" + " with name '%s'",
195                    klass.getName(), name), e);
196        }
197        return ret;
198    }
199
200    @Override
201    public PageProvider<?> getPageProvider(String name, DocumentModel searchDocument, List<SortInfo> sortInfos,
202            Long pageSize, Long currentPage, Map<String, Serializable> properties, Object... parameters) {
203        PageProviderDefinition desc = providerReg.getPageProvider(name);
204        if (desc == null) {
205            throw new NuxeoException(String.format("Could not resolve page provider with name '%s'", name));
206        }
207        return getPageProvider(name, desc, searchDocument, sortInfos, pageSize, currentPage, properties, null, null,
208                parameters);
209    }
210
211    @Override
212    public PageProvider<?> getPageProvider(String name, DocumentModel searchDocument, List<SortInfo> sortInfos,
213            Long pageSize, Long currentPage, Map<String, Serializable> properties, List<String> highlights,
214            List<QuickFilter> quickFilters, 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, highlights,
220                quickFilters, 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<QuickFilter> quickFilters,
226            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, quickFilters,
232                parameters);
233    }
234
235    @Override
236    public PageProvider<?> getPageProvider(String name, List<SortInfo> sortInfos, Long pageSize, Long currentPage,
237            Map<String, Serializable> properties, Object... parameters) {
238        return getPageProvider(name, (DocumentModel) null, sortInfos, pageSize, currentPage, properties, parameters);
239    }
240
241    @Override
242    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
243        if (PROVIDER_EP.equals(extensionPoint)) {
244            PageProviderDefinition desc = (PageProviderDefinition) contribution;
245            registerPageProviderDefinition(desc);
246        } else if (REPLACER_EP.equals(extensionPoint)) {
247            PageProviderClassReplacerDefinition desc = (PageProviderClassReplacerDefinition) contribution;
248            replacersReg.addContribution(desc);
249        }
250    }
251
252    @Override
253    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
254        if (PROVIDER_EP.equals(extensionPoint)) {
255            PageProviderDefinition desc = (PageProviderDefinition) contribution;
256            unregisterPageProviderDefinition(desc);
257        }
258    }
259
260    @Override
261    public void start(ComponentContext context) {
262        replacersReg.dumpReplacerMap();
263    }
264
265    @Override
266    public void registerPageProviderDefinition(PageProviderDefinition desc) {
267        providerReg.addContribution(desc);
268    }
269
270    @Override
271    public void unregisterPageProviderDefinition(PageProviderDefinition desc) {
272        providerReg.removeContribution(desc);
273    }
274
275    @Override
276    public Set<String> getPageProviderDefinitionNames() {
277        return Collections.unmodifiableSet(providerReg.providers.keySet());
278    }
279
280}