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}