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}