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<String, WidgetTypeDefinitionRegistry>();
098        layoutTypeDefsByCat = new HashMap<String, LayoutTypeDefinitionRegistry>();
099        widgetTypesByCat = new HashMap<String, WidgetTypeRegistry>();
100        layoutsByCat = new HashMap<String, LayoutDefinitionRegistry>();
101        widgetsByCat = new HashMap<String, WidgetDefinitionRegistry>();
102        widgetConvertersByCat = new HashMap<String, WidgetConverterRegistry>();
103        layoutConvertersByCat = new HashMap<String, LayoutConverterRegistry>();
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<String>();
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<String>();
252        res.addAll(cats);
253        Collections.sort(res);
254        return res;
255    }
256
257    // widget types
258
259    public void registerWidgetType(String category, WidgetTypeDefinition desc) {
260        String name = desc.getName();
261        String className = desc.getHandlerClassName();
262        Class<?> widgetTypeClass = null;
263        if (className != null) {
264            try {
265                widgetTypeClass = LayoutStoreImpl.class.getClassLoader().loadClass(className);
266            } catch (ReflectiveOperationException e) {
267                log.error("Caught error when instantiating widget type handler", e);
268                return;
269            }
270        }
271
272        // override only if handler class was resolved correctly
273        if (widgetTypesByCat.containsKey(name) || widgetTypeDefsByCat.containsKey(name)) {
274            log.warn(String.format("Overriding definition for widget type %s", name));
275            widgetTypesByCat.remove(name);
276            widgetTypeDefsByCat.remove(name);
277        }
278        WidgetTypeImpl widgetType = new WidgetTypeImpl(name, widgetTypeClass, desc.getProperties());
279        widgetType.setAliases(desc.getAliases());
280        WidgetTypeRegistry typeReg = widgetTypesByCat.get(category);
281        if (typeReg == null) {
282            typeReg = new WidgetTypeRegistry(category);
283            widgetTypesByCat.put(category, typeReg);
284        }
285        typeReg.addContribution(widgetType);
286        WidgetTypeDefinitionRegistry defReg = widgetTypeDefsByCat.get(category);
287        if (defReg == null) {
288            defReg = new WidgetTypeDefinitionRegistry(category);
289            widgetTypeDefsByCat.put(category, defReg);
290        }
291        defReg.addContribution(desc);
292        log.info(String.format("Registered widget type '%s' for category '%s' ", name, category));
293    }
294
295    public void unregisterWidgetType(String category, WidgetTypeDefinition desc) {
296        String name = desc.getName();
297        WidgetTypeRegistry typeReg = widgetTypesByCat.get(category);
298        WidgetTypeDefinitionRegistry defReg = widgetTypeDefsByCat.get(category);
299        if (typeReg != null && defReg != null) {
300            // remove corresponding widget type, only reuse name
301            WidgetType widgetType = new WidgetTypeImpl(name, null, null);
302            typeReg.removeContribution(widgetType);
303            defReg.removeContribution(desc);
304            log.info(String.format("Unregistered widget type '%s' for category '%s' ", name, category));
305        }
306    }
307
308    // layout types
309
310    public void registerLayoutType(String category, LayoutTypeDefinition layoutTypeDef) {
311        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
312        if (reg == null) {
313            reg = new LayoutTypeDefinitionRegistry(category);
314            layoutTypeDefsByCat.put(category, reg);
315        }
316        reg.addContribution(layoutTypeDef);
317        log.info(String.format("Registered layout type '%s' for category '%s' ", layoutTypeDef.getName(), category));
318    }
319
320    public void unregisterLayoutType(String category, LayoutTypeDefinition layoutTypeDef) {
321        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
322        if (reg != null) {
323            reg.removeContribution(layoutTypeDef);
324            log.info(String.format("Unregistered layout type '%s' for category '%s' ", layoutTypeDef.getName(),
325                    category));
326        }
327    }
328
329    // layouts
330
331    public void registerLayout(String category, LayoutDefinition layoutDef) {
332        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
333        if (reg == null) {
334            reg = new LayoutDefinitionRegistry(category);
335            layoutsByCat.put(category, reg);
336        }
337        reg.addContribution(layoutDef);
338        log.info(String.format("Registered layout '%s' for category '%s' ", layoutDef.getName(), category));
339    }
340
341    public void unregisterLayout(String category, LayoutDefinition layoutDef) {
342        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
343        if (reg != null) {
344            reg.removeContribution(layoutDef);
345            log.info(String.format("Unregistered layout '%s' for category '%s' ", layoutDef.getName(), category));
346        }
347    }
348
349    // widgets
350
351    public void registerWidget(String category, WidgetDefinition widgetDef) {
352        WidgetDefinitionRegistry reg = widgetsByCat.get(category);
353        if (reg == null) {
354            reg = new WidgetDefinitionRegistry(category);
355            widgetsByCat.put(category, reg);
356        }
357        reg.addContribution(widgetDef);
358        log.info(String.format("Registered widget '%s' for category '%s' ", widgetDef.getName(), category));
359    }
360
361    public void unregisterWidget(String category, WidgetDefinition widgetDef) {
362        WidgetDefinitionRegistry reg = widgetsByCat.get(category);
363        if (reg != null) {
364            reg.removeContribution(widgetDef);
365            log.info(String.format("Unregistered widget '%s' for category '%s' ", widgetDef.getName(), category));
366        }
367    }
368
369    // converter descriptors
370
371    public void registerLayoutConverter(String category, LayoutConverterDescriptor layoutConverter) {
372        LayoutConverterRegistry reg = layoutConvertersByCat.get(category);
373        if (reg == null) {
374            reg = new LayoutConverterRegistry(category);
375            layoutConvertersByCat.put(category, reg);
376        }
377        reg.addContribution(layoutConverter);
378        log.info(String.format("Registered layout converter '%s' for category '%s' ", layoutConverter.getName(),
379                category));
380    }
381
382    public void unregisterLayoutConverter(String category, LayoutConverterDescriptor layoutConverter) {
383        LayoutConverterRegistry reg = layoutConvertersByCat.get(category);
384        if (reg != null) {
385            reg.removeContribution(layoutConverter);
386            log.info(String.format("Unregistered layout converter '%s' for category '%s' ", layoutConverter.getName(),
387                    category));
388        }
389    }
390
391    public void registerWidgetConverter(String category, WidgetConverterDescriptor widgetConverter) {
392        WidgetConverterRegistry reg = widgetConvertersByCat.get(category);
393        if (reg == null) {
394            reg = new WidgetConverterRegistry(category);
395            widgetConvertersByCat.put(category, reg);
396        }
397        reg.addContribution(widgetConverter);
398        log.info(String.format("Registered widget converter '%s' for category '%s' ", widgetConverter.getName(),
399                category));
400    }
401
402    public void unregisterWidgetConverter(String category, WidgetConverterDescriptor widgetConverter) {
403        WidgetConverterRegistry reg = widgetConvertersByCat.get(category);
404        if (reg != null) {
405            reg.removeContribution(widgetConverter);
406            log.info(String.format("Unregistered widget converter '%s' for category '%s' ", widgetConverter.getName(),
407                    category));
408        }
409    }
410
411    // service api
412
413    public WidgetType getWidgetType(String category, String typeName) {
414        WidgetTypeRegistry reg = widgetTypesByCat.get(category);
415        if (reg != null) {
416            return reg.getWidgetType(typeName);
417        }
418        return null;
419    }
420
421    @Override
422    public WidgetTypeDefinition getWidgetTypeDefinition(String category, String typeName) {
423        WidgetTypeDefinitionRegistry reg = widgetTypeDefsByCat.get(category);
424        if (reg != null) {
425            return reg.getDefinition(typeName);
426        }
427        return null;
428    }
429
430    @Override
431    public List<WidgetTypeDefinition> getWidgetTypeDefinitions(String category) {
432        List<WidgetTypeDefinition> res = new ArrayList<WidgetTypeDefinition>();
433        WidgetTypeDefinitionRegistry reg = widgetTypeDefsByCat.get(category);
434        if (reg != null) {
435            Collection<WidgetTypeDefinition> defs = reg.getDefinitions();
436            if (defs != null) {
437                res.addAll(defs);
438            }
439        }
440        return res;
441    }
442
443    @Override
444    public LayoutTypeDefinition getLayoutTypeDefinition(String category, String typeName) {
445        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
446        if (reg != null) {
447            return reg.getDefinition(typeName);
448        }
449        return null;
450    }
451
452    @Override
453    public List<LayoutTypeDefinition> getLayoutTypeDefinitions(String category) {
454        List<LayoutTypeDefinition> res = new ArrayList<LayoutTypeDefinition>();
455        LayoutTypeDefinitionRegistry reg = layoutTypeDefsByCat.get(category);
456        if (reg != null) {
457            Collection<LayoutTypeDefinition> defs = reg.getDefinitions();
458            if (defs != null) {
459                res.addAll(defs);
460            }
461        }
462        return res;
463    }
464
465    public LayoutDefinition getLayoutDefinition(String category, String layoutName) {
466        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
467        if (reg != null) {
468            return reg.getLayoutDefinition(layoutName);
469        }
470        return null;
471    }
472
473    public List<String> getLayoutDefinitionNames(String category) {
474        LayoutDefinitionRegistry reg = layoutsByCat.get(category);
475        if (reg != null) {
476            return reg.getLayoutNames();
477        }
478        return Collections.emptyList();
479    }
480
481    public WidgetDefinition getWidgetDefinition(String category, String widgetName) {
482        WidgetDefinitionRegistry reg = widgetsByCat.get(category);
483        if (reg != null) {
484            return reg.getWidgetDefinition(widgetName);
485        }
486        return null;
487    }
488
489    @Override
490    public List<LayoutDefinitionConverter> getLayoutConverters(String category) {
491        List<LayoutDefinitionConverter> res = new ArrayList<LayoutDefinitionConverter>();
492        List<String> orderedConverterNames = new ArrayList<String>();
493        LayoutConverterRegistry reg = layoutConvertersByCat.get(category);
494        if (reg != null) {
495            List<LayoutConverterDescriptor> descs = reg.getConverters();
496            // first sort by order
497            Collections.sort(descs);
498            // instantiate converter instances
499            for (LayoutConverterDescriptor desc : descs) {
500                Class<?> converterClass;
501                try {
502                    converterClass = LayoutStoreImpl.class.getClassLoader().loadClass(desc.getConverterClassName());
503                    LayoutDefinitionConverter converter = (LayoutDefinitionConverter) converterClass.newInstance();
504                    res.add(converter);
505                    orderedConverterNames.add(desc.getName());
506                } catch (ReflectiveOperationException e) {
507                    log.error("Caught error when instantiating " + "layout definition converter", e);
508                }
509            }
510        }
511        if (log.isDebugEnabled()) {
512            log.debug(String.format("Ordered layout converters for category '%s': %s", category, orderedConverterNames));
513        }
514        return res;
515    }
516
517    @Override
518    public List<WidgetDefinitionConverter> getWidgetConverters(String category) {
519        List<WidgetDefinitionConverter> res = new ArrayList<WidgetDefinitionConverter>();
520        List<String> orderedConverterNames = new ArrayList<String>();
521        WidgetConverterRegistry reg = widgetConvertersByCat.get(category);
522        if (reg != null) {
523            List<WidgetConverterDescriptor> descs = reg.getConverters();
524            // first sort by order
525            Collections.sort(descs);
526            // instantiate converter instances
527            for (WidgetConverterDescriptor desc : descs) {
528                Class<?> converterClass;
529                try {
530                    converterClass = LayoutStoreImpl.class.getClassLoader().loadClass(desc.getConverterClassName());
531                    WidgetDefinitionConverter converter = (WidgetDefinitionConverter) converterClass.newInstance();
532                    res.add(converter);
533                    orderedConverterNames.add(desc.getName());
534                } catch (ReflectiveOperationException e) {
535                    log.error("Caught error when instantiating " + "widget definition converter", e);
536                }
537            }
538        }
539        if (log.isDebugEnabled()) {
540            log.debug(String.format("Ordered widget converters for category '%s': %s", category, orderedConverterNames));
541        }
542        return res;
543    }
544
545}