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