001/*
002 * (C) Copyright 2010 Nuxeo SA (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.forms.layout.core.service;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.ecm.platform.forms.layout.api.LayoutDefinition;
033import org.nuxeo.ecm.platform.forms.layout.api.LayoutTypeDefinition;
034import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition;
035import org.nuxeo.ecm.platform.forms.layout.api.WidgetType;
036import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeDefinition;
037import org.nuxeo.ecm.platform.forms.layout.api.converters.LayoutDefinitionConverter;
038import org.nuxeo.ecm.platform.forms.layout.api.converters.WidgetDefinitionConverter;
039import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetTypeImpl;
040import org.nuxeo.ecm.platform.forms.layout.api.service.LayoutStore;
041import org.nuxeo.ecm.platform.forms.layout.core.registries.LayoutConverterRegistry;
042import org.nuxeo.ecm.platform.forms.layout.core.registries.LayoutDefinitionRegistry;
043import org.nuxeo.ecm.platform.forms.layout.core.registries.LayoutTypeDefinitionRegistry;
044import org.nuxeo.ecm.platform.forms.layout.core.registries.WidgetConverterRegistry;
045import org.nuxeo.ecm.platform.forms.layout.core.registries.WidgetDefinitionRegistry;
046import org.nuxeo.ecm.platform.forms.layout.core.registries.WidgetTypeDefinitionRegistry;
047import org.nuxeo.ecm.platform.forms.layout.core.registries.WidgetTypeRegistry;
048import org.nuxeo.ecm.platform.forms.layout.descriptors.LayoutConverterDescriptor;
049import org.nuxeo.ecm.platform.forms.layout.descriptors.LayoutDescriptor;
050import org.nuxeo.ecm.platform.forms.layout.descriptors.LayoutTypeDescriptor;
051import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetConverterDescriptor;
052import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetDescriptor;
053import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetTypeDescriptor;
054import org.nuxeo.runtime.model.ComponentInstance;
055import org.nuxeo.runtime.model.DefaultComponent;
056
057/**
058 * @author Anahide Tchertchian
059 * @since 5.5
060 */
061public class LayoutStoreImpl extends DefaultComponent implements LayoutStore {
062
063    private static final Log log = LogFactory.getLog(LayoutStoreImpl.class);
064
065    private static final long serialVersionUID = 1L;
066
067    public static final String WIDGET_TYPES_EP_NAME = "widgettypes";
068
069    /**
070     * @since 6.0
071     */
072    public static final String LAYOUT_TYPES_EP_NAME = "layouttypes";
073
074    public static final String WIDGETS_EP_NAME = "widgets";
075
076    public static final String LAYOUTS_EP_NAME = "layouts";
077
078    public static final String LAYOUT_CONVERTERS_EP_NAME = "layoutConverters";
079
080    public static final String WIDGET_CONVERTERS_EP_NAME = "widgetConverters";
081
082    protected final Map<String, WidgetTypeRegistry> widgetTypesByCat;
083
084    protected final Map<String, WidgetTypeDefinitionRegistry> widgetTypeDefsByCat;
085
086    protected final Map<String, LayoutTypeDefinitionRegistry> layoutTypeDefsByCat;
087
088    protected final Map<String, LayoutDefinitionRegistry> layoutsByCat;
089
090    protected final Map<String, WidgetDefinitionRegistry> widgetsByCat;
091
092    protected final Map<String, WidgetConverterRegistry> widgetConvertersByCat;
093
094    protected final Map<String, LayoutConverterRegistry> layoutConvertersByCat;
095
096    public LayoutStoreImpl() {
097        widgetTypeDefsByCat = new HashMap<>();
098        layoutTypeDefsByCat = new HashMap<>();
099        widgetTypesByCat = new HashMap<>();
100        layoutsByCat = new HashMap<>();
101        widgetsByCat = new HashMap<>();
102        widgetConvertersByCat = new HashMap<>();
103        layoutConvertersByCat = new HashMap<>();
104    }
105
106    // Runtime component API
107
108    @Override
109    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
110        if (extensionPoint.equals(WIDGET_TYPES_EP_NAME)) {
111            WidgetTypeDescriptor desc = (WidgetTypeDescriptor) contribution;
112            String[] categories = desc.getCategories();
113            if (categories == null || categories.length == 0) {
114                log.error(String.format("Cannot register widget type '%s': no category found", desc.getName()));
115            } else {
116                for (String cat : categories) {
117                    registerWidgetType(cat, desc.getWidgetTypeDefinition());
118                }
119            }
120        } else if (extensionPoint.equals(LAYOUT_TYPES_EP_NAME)) {
121            LayoutTypeDescriptor desc = (LayoutTypeDescriptor) contribution;
122            String[] categories = desc.getCategories();
123            if (categories == null || categories.length == 0) {
124                log.error(String.format("Cannot register layout type '%s': no category found", desc.getName()));
125            } else {
126                for (String cat : categories) {
127                    registerLayoutType(cat, desc.getLayoutTypeDefinition());
128                }
129            }
130        } else if (extensionPoint.equals(LAYOUTS_EP_NAME)) {
131            LayoutDescriptor desc = (LayoutDescriptor) contribution;
132            String[] categories = desc.getCategories();
133            if (categories == null || categories.length == 0) {
134                log.error(String.format("Cannot register layout '%s': no category found", desc.getName()));
135            } else {
136                for (String cat : categories) {
137                    registerLayout(cat, desc.getLayoutDefinition());
138                }
139            }
140        } else if (extensionPoint.equals(WIDGETS_EP_NAME)) {
141            WidgetDescriptor desc = (WidgetDescriptor) contribution;
142            String[] categories = desc.getCategories();
143            if (categories == null || categories.length == 0) {
144                log.error(String.format("Cannot register widget '%s': no category found", desc.getName()));
145            } else {
146                for (String cat : categories) {
147                    registerWidget(cat, desc.getWidgetDefinition());
148                }
149            }
150        } else if (extensionPoint.equals(LAYOUT_CONVERTERS_EP_NAME)) {
151            LayoutConverterDescriptor desc = (LayoutConverterDescriptor) contribution;
152            String[] categories = desc.getCategories();
153            if (categories == null || categories.length == 0) {
154                log.error(String.format("Cannot register layout converter '%s': no category found", desc.getName()));
155            } else {
156                for (String cat : categories) {
157                    registerLayoutConverter(cat, desc);
158                }
159            }
160        } else if (extensionPoint.equals(WIDGET_CONVERTERS_EP_NAME)) {
161            WidgetConverterDescriptor desc = (WidgetConverterDescriptor) contribution;
162            String[] categories = desc.getCategories();
163            if (categories == null || categories.length == 0) {
164                log.error(String.format("Cannot register widget converter '%s': no category found", desc.getName()));
165            } else {
166                for (String cat : categories) {
167                    registerWidgetConverter(cat, desc);
168                }
169            }
170        } else {
171            log.error(String.format("Unknown extension point %s, can't register !", extensionPoint));
172        }
173    }
174
175    @Override
176    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
177        if (extensionPoint.equals(WIDGET_TYPES_EP_NAME)) {
178            WidgetTypeDescriptor desc = (WidgetTypeDescriptor) contribution;
179            String[] categories = desc.getCategories();
180            if (categories == null || categories.length == 0) {
181                log.error(String.format("Cannot unregister widget type '%s': no category found", desc.getName()));
182            } else {
183                for (String cat : categories) {
184                    unregisterWidgetType(cat, desc.getWidgetTypeDefinition());
185                }
186            }
187        } else if (extensionPoint.equals(LAYOUT_TYPES_EP_NAME)) {
188            LayoutTypeDescriptor desc = (LayoutTypeDescriptor) contribution;
189            String[] categories = desc.getCategories();
190            if (categories == null || categories.length == 0) {
191                log.error(String.format("Cannot unregister layout type '%s': no category found", desc.getName()));
192            } else {
193                for (String cat : categories) {
194                    unregisterLayoutType(cat, desc.getLayoutTypeDefinition());
195                }
196            }
197        } else if (extensionPoint.equals(LAYOUTS_EP_NAME)) {
198            LayoutDescriptor desc = (LayoutDescriptor) contribution;
199            String[] categories = desc.getCategories();
200            if (categories == null || categories.length == 0) {
201                log.error(String.format("Cannot unregister layout '%s': no category found", desc.getName()));
202            } else {
203                for (String cat : categories) {
204                    unregisterLayout(cat, desc.getLayoutDefinition());
205                }
206            }
207        } else if (extensionPoint.equals(WIDGETS_EP_NAME)) {
208            WidgetDescriptor desc = (WidgetDescriptor) contribution;
209            String[] categories = desc.getCategories();
210            if (categories == null || categories.length == 0) {
211                log.error(String.format("Cannot unregister widget '%s': no category found", desc.getName()));
212            } else {
213                for (String cat : categories) {
214                    unregisterWidget(cat, desc.getWidgetDefinition());
215                }
216            }
217        } else if (extensionPoint.equals(LAYOUT_CONVERTERS_EP_NAME)) {
218            LayoutConverterDescriptor desc = (LayoutConverterDescriptor) contribution;
219            String[] categories = desc.getCategories();
220            if (categories == null || categories.length == 0) {
221                log.error(String.format("Cannot register layout converter '%s': no category found", desc.getName()));
222            } else {
223                for (String cat : categories) {
224                    unregisterLayoutConverter(cat, desc);
225                }
226            }
227        } else if (extensionPoint.equals(WIDGET_CONVERTERS_EP_NAME)) {
228            WidgetConverterDescriptor desc = (WidgetConverterDescriptor) contribution;
229            String[] categories = desc.getCategories();
230            if (categories == null || categories.length == 0) {
231                log.error(String.format("Cannot register widget converter '%s': no category found", desc.getName()));
232            } else {
233                for (String cat : categories) {
234                    unregisterWidgetConverter(cat, desc);
235                }
236            }
237        } else {
238            log.error(String.format("Unknown extension point %s, can't unregister !", extensionPoint));
239        }
240    }
241
242    // Categories
243
244    @Override
245    public List<String> getCategories() {
246        Set<String> cats = new HashSet<>();
247        cats.addAll(widgetTypeDefsByCat.keySet());
248        cats.addAll(widgetTypesByCat.keySet());
249        cats.addAll(layoutsByCat.keySet());
250        cats.addAll(widgetsByCat.keySet());
251        List<String> res = new ArrayList<>();
252        res.addAll(cats);
253        Collections.sort(res);
254        return res;
255    }
256
257    // widget types
258
259    @Override
260    public void registerWidgetType(String category, WidgetTypeDefinition desc) {
261        String name = desc.getName();
262        String className = desc.getHandlerClassName();
263        Class<?> widgetTypeClass = null;
264        if (className != null) {
265            try {
266                widgetTypeClass = LayoutStoreImpl.class.getClassLoader().loadClass(className);
267            } catch (ReflectiveOperationException e) {
268                log.error("Caught error when instantiating widget type handler", e);
269                return;
270            }
271        }
272
273        // override only if handler class was resolved correctly
274        if (widgetTypesByCat.containsKey(name) || widgetTypeDefsByCat.containsKey(name)) {
275            log.warn(String.format("Overriding definition for widget type %s", name));
276            widgetTypesByCat.remove(name);
277            widgetTypeDefsByCat.remove(name);
278        }
279        WidgetTypeImpl widgetType = new WidgetTypeImpl(name, widgetTypeClass, desc.getProperties());
280        widgetType.setAliases(desc.getAliases());
281        WidgetTypeRegistry typeReg = widgetTypesByCat.get(category);
282        if (typeReg == null) {
283            typeReg = new WidgetTypeRegistry(category);
284            widgetTypesByCat.put(category, typeReg);
285        }
286        typeReg.addContribution(widgetType);
287        WidgetTypeDefinitionRegistry defReg = widgetTypeDefsByCat.get(category);
288        if (defReg == null) {
289            defReg = new WidgetTypeDefinitionRegistry(category);
290            widgetTypeDefsByCat.put(category, defReg);
291        }
292        defReg.addContribution(desc);
293        log.info(String.format("Registered widget type '%s' for category '%s' ", name, category));
294    }
295
296    @Override
297    public void unregisterWidgetType(String category, WidgetTypeDefinition desc) {
298        String name = desc.getName();
299        WidgetTypeRegistry typeReg = widgetTypesByCat.get(category);
300        WidgetTypeDefinitionRegistry defReg = widgetTypeDefsByCat.get(category);
301        if (typeReg != null && defReg != null) {
302            // remove corresponding widget type, only reuse name
303            WidgetType widgetType = new WidgetTypeImpl(name, null, null);
304            typeReg.removeContribution(widgetType);
305            defReg.removeContribution(desc);
306            log.info(String.format("Unregistered widget type '%s' for category '%s' ", name, category));
307        }
308    }
309
310    // layout types
311
312    @Override
313    public void registerLayoutType(String category, LayoutTypeDefinition layoutTypeDef) {
314        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
315        if (reg == null) {
316            reg = new LayoutTypeDefinitionRegistry(category);
317            layoutTypeDefsByCat.put(category, reg);
318        }
319        reg.addContribution(layoutTypeDef);
320        log.info(String.format("Registered layout type '%s' for category '%s' ", layoutTypeDef.getName(), category));
321    }
322
323    @Override
324    public void unregisterLayoutType(String category, LayoutTypeDefinition layoutTypeDef) {
325        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
326        if (reg != null) {
327            reg.removeContribution(layoutTypeDef);
328            log.info(String.format("Unregistered layout type '%s' for category '%s' ", layoutTypeDef.getName(),
329                    category));
330        }
331    }
332
333    // layouts
334
335    @Override
336    public void registerLayout(String category, LayoutDefinition layoutDef) {
337        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
338        if (reg == null) {
339            reg = new LayoutDefinitionRegistry(category);
340            layoutsByCat.put(category, reg);
341        }
342        reg.addContribution(layoutDef);
343        log.info(String.format("Registered layout '%s' for category '%s' ", layoutDef.getName(), category));
344    }
345
346    @Override
347    public void unregisterLayout(String category, LayoutDefinition layoutDef) {
348        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
349        if (reg != null) {
350            reg.removeContribution(layoutDef);
351            log.info(String.format("Unregistered layout '%s' for category '%s' ", layoutDef.getName(), category));
352        }
353    }
354
355    // widgets
356
357    @Override
358    public void registerWidget(String category, WidgetDefinition widgetDef) {
359        WidgetDefinitionRegistry reg = widgetsByCat.get(category);
360        if (reg == null) {
361            reg = new WidgetDefinitionRegistry(category);
362            widgetsByCat.put(category, reg);
363        }
364        reg.addContribution(widgetDef);
365        log.info(String.format("Registered widget '%s' for category '%s' ", widgetDef.getName(), category));
366    }
367
368    @Override
369    public void unregisterWidget(String category, WidgetDefinition widgetDef) {
370        WidgetDefinitionRegistry reg = widgetsByCat.get(category);
371        if (reg != null) {
372            reg.removeContribution(widgetDef);
373            log.info(String.format("Unregistered widget '%s' for category '%s' ", widgetDef.getName(), category));
374        }
375    }
376
377    // converter descriptors
378
379    public void registerLayoutConverter(String category, LayoutConverterDescriptor layoutConverter) {
380        LayoutConverterRegistry reg = layoutConvertersByCat.get(category);
381        if (reg == null) {
382            reg = new LayoutConverterRegistry(category);
383            layoutConvertersByCat.put(category, reg);
384        }
385        reg.addContribution(layoutConverter);
386        log.info(String.format("Registered layout converter '%s' for category '%s' ", layoutConverter.getName(),
387                category));
388    }
389
390    public void unregisterLayoutConverter(String category, LayoutConverterDescriptor layoutConverter) {
391        LayoutConverterRegistry reg = layoutConvertersByCat.get(category);
392        if (reg != null) {
393            reg.removeContribution(layoutConverter);
394            log.info(String.format("Unregistered layout converter '%s' for category '%s' ", layoutConverter.getName(),
395                    category));
396        }
397    }
398
399    public void registerWidgetConverter(String category, WidgetConverterDescriptor widgetConverter) {
400        WidgetConverterRegistry reg = widgetConvertersByCat.get(category);
401        if (reg == null) {
402            reg = new WidgetConverterRegistry(category);
403            widgetConvertersByCat.put(category, reg);
404        }
405        reg.addContribution(widgetConverter);
406        log.info(String.format("Registered widget converter '%s' for category '%s' ", widgetConverter.getName(),
407                category));
408    }
409
410    public void unregisterWidgetConverter(String category, WidgetConverterDescriptor widgetConverter) {
411        WidgetConverterRegistry reg = widgetConvertersByCat.get(category);
412        if (reg != null) {
413            reg.removeContribution(widgetConverter);
414            log.info(String.format("Unregistered widget converter '%s' for category '%s' ", widgetConverter.getName(),
415                    category));
416        }
417    }
418
419    // service api
420
421    @Override
422    public WidgetType getWidgetType(String category, String typeName) {
423        WidgetTypeRegistry reg = widgetTypesByCat.get(category);
424        if (reg != null) {
425            return reg.getWidgetType(typeName);
426        }
427        return null;
428    }
429
430    @Override
431    public WidgetTypeDefinition getWidgetTypeDefinition(String category, String typeName) {
432        WidgetTypeDefinitionRegistry reg = widgetTypeDefsByCat.get(category);
433        if (reg != null) {
434            return reg.getDefinition(typeName);
435        }
436        return null;
437    }
438
439    @Override
440    public List<WidgetTypeDefinition> getWidgetTypeDefinitions(String category) {
441        List<WidgetTypeDefinition> res = new ArrayList<>();
442        WidgetTypeDefinitionRegistry reg = widgetTypeDefsByCat.get(category);
443        if (reg != null) {
444            Collection<WidgetTypeDefinition> defs = reg.getDefinitions();
445            if (defs != null) {
446                res.addAll(defs);
447            }
448        }
449        return res;
450    }
451
452    @Override
453    public LayoutTypeDefinition getLayoutTypeDefinition(String category, String typeName) {
454        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
455        if (reg != null) {
456            return reg.getDefinition(typeName);
457        }
458        return null;
459    }
460
461    @Override
462    public List<LayoutTypeDefinition> getLayoutTypeDefinitions(String category) {
463        List<LayoutTypeDefinition> res = new ArrayList<>();
464        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
465        if (reg != null) {
466            Collection<LayoutTypeDefinition> defs = reg.getDefinitions();
467            if (defs != null) {
468                res.addAll(defs);
469            }
470        }
471        return res;
472    }
473
474    @Override
475    public LayoutDefinition getLayoutDefinition(String category, String layoutName) {
476        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
477        if (reg != null) {
478            return reg.getLayoutDefinition(layoutName);
479        }
480        return null;
481    }
482
483    @Override
484    public List<String> getLayoutDefinitionNames(String category) {
485        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
486        if (reg != null) {
487            return reg.getLayoutNames();
488        }
489        return Collections.emptyList();
490    }
491
492    @Override
493    public WidgetDefinition getWidgetDefinition(String category, String widgetName) {
494        WidgetDefinitionRegistry reg = widgetsByCat.get(category);
495        if (reg != null) {
496            return reg.getWidgetDefinition(widgetName);
497        }
498        return null;
499    }
500
501    @Override
502    public List<LayoutDefinitionConverter> getLayoutConverters(String category) {
503        List<LayoutDefinitionConverter> res = new ArrayList<>();
504        List<String> orderedConverterNames = new ArrayList<>();
505        LayoutConverterRegistry reg = layoutConvertersByCat.get(category);
506        if (reg != null) {
507            List<LayoutConverterDescriptor> descs = reg.getConverters();
508            // first sort by order
509            Collections.sort(descs);
510            // instantiate converter instances
511            for (LayoutConverterDescriptor desc : descs) {
512                Class<?> converterClass;
513                try {
514                    converterClass = LayoutStoreImpl.class.getClassLoader().loadClass(desc.getConverterClassName());
515                    LayoutDefinitionConverter converter = (LayoutDefinitionConverter) converterClass.getDeclaredConstructor()
516                                                                                                    .newInstance();
517                    res.add(converter);
518                    orderedConverterNames.add(desc.getName());
519                } catch (ReflectiveOperationException e) {
520                    log.error("Caught error when instantiating " + "layout definition converter", e);
521                }
522            }
523        }
524        if (log.isDebugEnabled()) {
525            log.debug(String.format("Ordered layout converters for category '%s': %s", category, orderedConverterNames));
526        }
527        return res;
528    }
529
530    @Override
531    public List<WidgetDefinitionConverter> getWidgetConverters(String category) {
532        List<WidgetDefinitionConverter> res = new ArrayList<>();
533        List<String> orderedConverterNames = new ArrayList<>();
534        WidgetConverterRegistry reg = widgetConvertersByCat.get(category);
535        if (reg != null) {
536            List<WidgetConverterDescriptor> descs = reg.getConverters();
537            // first sort by order
538            Collections.sort(descs);
539            // instantiate converter instances
540            for (WidgetConverterDescriptor desc : descs) {
541                Class<?> converterClass;
542                try {
543                    converterClass = LayoutStoreImpl.class.getClassLoader().loadClass(desc.getConverterClassName());
544                    WidgetDefinitionConverter converter = (WidgetDefinitionConverter) converterClass.getDeclaredConstructor()
545                                                                                                    .newInstance();
546                    res.add(converter);
547                    orderedConverterNames.add(desc.getName());
548                } catch (ReflectiveOperationException e) {
549                    log.error("Caught error when instantiating " + "widget definition converter", e);
550                }
551            }
552        }
553        if (log.isDebugEnabled()) {
554            log.debug(String.format("Ordered widget converters for category '%s': %s", category, orderedConverterNames));
555        }
556        return res;
557    }
558
559}