001/* 002 * (C) Copyright 2006-2007 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 * Jean-Marc Orliaguet, Chalmers 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.theme.services; 023 024import java.io.File; 025import java.io.IOException; 026import java.net.MalformedURLException; 027import java.net.URL; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.nuxeo.runtime.model.ComponentContext; 035import org.nuxeo.runtime.model.ComponentName; 036import org.nuxeo.runtime.model.DefaultComponent; 037import org.nuxeo.runtime.model.Extension; 038import org.nuxeo.runtime.model.RuntimeContext; 039import org.nuxeo.theme.ApplicationType; 040import org.nuxeo.theme.CachingDef; 041import org.nuxeo.theme.Manager; 042import org.nuxeo.theme.NegotiationDef; 043import org.nuxeo.theme.Registrable; 044import org.nuxeo.theme.RegistryType; 045import org.nuxeo.theme.ViewDef; 046import org.nuxeo.theme.engines.EngineType; 047import org.nuxeo.theme.models.ModelType; 048import org.nuxeo.theme.perspectives.PerspectiveType; 049import org.nuxeo.theme.presets.PaletteParser; 050import org.nuxeo.theme.presets.PaletteType; 051import org.nuxeo.theme.presets.PresetType; 052import org.nuxeo.theme.resources.BankImport; 053import org.nuxeo.theme.resources.BankManager; 054import org.nuxeo.theme.resources.ResourceBank; 055import org.nuxeo.theme.resources.ResourceType; 056import org.nuxeo.theme.templates.TemplateEngineType; 057import org.nuxeo.theme.themes.ThemeDescriptor; 058import org.nuxeo.theme.themes.ThemeIOException; 059import org.nuxeo.theme.themes.ThemeManager; 060import org.nuxeo.theme.themes.ThemeParser; 061import org.nuxeo.theme.themes.ThemeSet; 062import org.nuxeo.theme.themes.ThemeSetEntry; 063import org.nuxeo.theme.types.Type; 064import org.nuxeo.theme.types.TypeFamily; 065import org.nuxeo.theme.types.TypeRegistry; 066import org.nuxeo.theme.views.ViewType; 067 068public class ThemeService extends DefaultComponent { 069 070 public static final ComponentName ID = new ComponentName("org.nuxeo.theme.services.ThemeService"); 071 072 private static final Log log = LogFactory.getLog(ThemeService.class); 073 074 private Map<String, Registrable> registries = new HashMap<String, Registrable>(); 075 076 private RuntimeContext context; 077 078 public Map<String, Registrable> getRegistries() { 079 return registries; 080 } 081 082 public Registrable getRegistry(String name) { 083 return registries.get(name); 084 } 085 086 public synchronized void addRegistry(String name, Registrable registry) { 087 registries.put(name, registry); 088 } 089 090 public synchronized void removeRegistry(String name) { 091 Registrable registry = registries.get(name); 092 if (registry != null) { 093 registry.clear(); 094 } 095 registries.remove(name); 096 } 097 098 @Override 099 public void activate(ComponentContext ctx) { 100 context = ctx.getRuntimeContext(); 101 Manager.initializeProtocols(); 102 log.debug("Theme service activated"); 103 } 104 105 @Override 106 public void deactivate(ComponentContext ctx) { 107 for (Registrable registry : registries.values()) { 108 registry.clear(); 109 } 110 registries.clear(); 111 context = null; 112 Manager.resetProtocols(); 113 log.debug("Theme service deactivated"); 114 } 115 116 @Override 117 public void applicationStarted(ComponentContext context) { 118 // themes registered as contributions 119 for (ThemeDescriptor themeDescriptor : ThemeManager.getThemeDescriptors()) { 120 registerTheme(themeDescriptor); 121 } 122 // custom themes located on the file-system 123 registerCustomThemes(); 124 125 ThemeManager.updateThemeDescriptors(); 126 127 // setup resource banks 128 BankManager.setupBanks(); 129 } 130 131 @Override 132 public void registerExtension(Extension extension) { 133 String xp = extension.getExtensionPoint(); 134 if (xp.equals("registries")) { 135 registerRegistryExtension(extension); 136 } else if (xp.equals("elements") || xp.equals("fragments") || xp.equals("formats") 137 || xp.equals("format-filters") || xp.equals("standalone-filters") || xp.equals("negotiations") 138 || xp.equals("shortcuts") || xp.equals("vocabularies")) { 139 registerTypeExtension(extension); 140 } else if (xp.equals("applications")) { 141 registerApplicationExtension(extension); 142 } else if (xp.equals("perspectives")) { 143 registerPerspectiveExtension(extension); 144 } else if (xp.equals("engines")) { 145 registerEngineExtension(extension); 146 } else if (xp.equals("template-engines")) { 147 registerTemplateEngineExtension(extension); 148 } else if (xp.equals("themes")) { 149 registerThemeExtension(extension); 150 } else if (xp.equals("themesets")) { 151 registerThemeSetExtension(extension); 152 } else if (xp.equals("presets")) { 153 registerPresetExtension(extension); 154 } else if (xp.equals("views")) { 155 registerViewExtension(extension); 156 } else if (xp.equals("models")) { 157 registerModelExtension(extension); 158 } else if (xp.equals("resources")) { 159 registerResourceExtension(extension); 160 } else if (xp.equals("banks")) { 161 registerBank(extension); 162 } else { 163 log.warn(String.format("Unknown extension point: %s", xp)); 164 } 165 } 166 167 @Override 168 public void unregisterExtension(Extension extension) { 169 String xp = extension.getExtensionPoint(); 170 if (xp.equals("registries")) { 171 unregisterRegistryExtension(extension); 172 } else if (xp.equals("elements") || xp.equals("fragments") || xp.equals("formats") 173 || xp.equals("format-filters") || xp.equals("standalone-filters") || xp.equals("engines") 174 || xp.equals("template-engines") || xp.equals("negotiations") || xp.equals("perspectives") 175 || xp.equals("applications") || xp.equals("shortcuts") || xp.equals("vocabularies") 176 || (xp.equals("presets")) || xp.equals("views") || xp.equals("themes") || xp.equals("themesets")) { 177 unregisterTypeExtension(extension); 178 } else if (xp.equals("resources")) { 179 unregisterResourceExtension(extension); 180 } else if (xp.equals("views")) { 181 unregisterViewExtension(extension); 182 } else if (xp.equals("models")) { 183 unregisterModelExtension(extension); 184 } else if (xp.equals("banks")) { 185 unregisterBank(extension); 186 } else { 187 log.warn(String.format("Unknown extension point: %s", xp)); 188 } 189 } 190 191 private void registerRegistryExtension(Extension extension) { 192 Object[] contribs = extension.getContributions(); 193 for (Object contrib : contribs) { 194 RegistryType registryType = (RegistryType) contrib; 195 Registrable registry = null; 196 try { 197 registry = (Registrable) context.loadClass(registryType.getClassName()).newInstance(); 198 } catch (ReflectiveOperationException e) { 199 log.warn("Could not create registry: " + registryType.getName() + "(" + registryType.getClassName() 200 + ")"); 201 } 202 if (registry != null) { 203 addRegistry(registryType.getName(), registry); 204 } 205 } 206 } 207 208 private void unregisterRegistryExtension(Extension extension) { 209 Object[] contribs = extension.getContributions(); 210 for (Object contrib : contribs) { 211 RegistryType registryType = (RegistryType) contrib; 212 removeRegistry(registryType.getName()); 213 } 214 } 215 216 private void registerTypeExtension(Extension extension) { 217 Object[] contribs = extension.getContributions(); 218 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 219 for (Object contrib : contribs) { 220 typeRegistry.register((Type) contrib); 221 } 222 } 223 224 private void unregisterTypeExtension(Extension extension) { 225 Object[] contribs = extension.getContributions(); 226 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 227 if (typeRegistry != null) { 228 for (Object contrib : contribs) { 229 typeRegistry.unregister((Type) contrib); 230 } 231 } 232 } 233 234 private void registerApplicationExtension(Extension extension) { 235 Object[] contribs = extension.getContributions(); 236 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 237 for (Object contrib : contribs) { 238 ApplicationType application = (ApplicationType) contrib; 239 240 ApplicationType oldApplication = (ApplicationType) typeRegistry.lookup(TypeFamily.APPLICATION, 241 application.getTypeName()); 242 243 if (oldApplication == null) { 244 String templateEngine = application.getTemplateEngine(); 245 if (templateEngine == null) { 246 final String defaultTemplateEngine = ThemeManager.getDefaultTemplateEngineName(); 247 log.warn(String.format( 248 "Please set the 'template-engine' attribute on <application root=\"%s\" template-engine=\"...\"> (default is '%s')", 249 application.getRoot(), defaultTemplateEngine)); 250 application.setTemplateEngine(defaultTemplateEngine); 251 } 252 typeRegistry.register(application); 253 254 } else { 255 // Merge properties from already registered application 256 final String templateEngine = application.getTemplateEngine(); 257 if (templateEngine != null) { 258 oldApplication.setTemplateEngine(templateEngine); 259 } 260 261 NegotiationDef negotiation = application.getNegotiation(); 262 if (negotiation != null) { 263 NegotiationDef oldNegotiation = oldApplication.getNegotiation(); 264 if (oldNegotiation == null) { 265 oldNegotiation = new NegotiationDef(); 266 oldApplication.setNegotiation(oldNegotiation); 267 } 268 if (negotiation.getStrategy() != null) { 269 oldNegotiation.setStrategy(negotiation.getStrategy()); 270 } 271 if (negotiation.getDefaultTheme() != null) { 272 oldNegotiation.setDefaultTheme(negotiation.getDefaultTheme()); 273 } 274 if (negotiation.getDefaultPerspective() != null) { 275 oldNegotiation.setDefaultPerspective(negotiation.getDefaultPerspective()); 276 } 277 if (negotiation.getDefaultEngine() != null) { 278 oldNegotiation.setDefaultEngine(negotiation.getDefaultEngine()); 279 } 280 } 281 282 CachingDef resourceCaching = application.getResourceCaching(); 283 if (resourceCaching != null) { 284 CachingDef oldResourceCaching = oldApplication.getResourceCaching(); 285 if (oldResourceCaching == null) { 286 oldResourceCaching = new CachingDef(); 287 oldApplication.setResourceCaching(oldResourceCaching); 288 } 289 if (resourceCaching.getLifetime() != null) { 290 oldResourceCaching.setLifetime(resourceCaching.getLifetime()); 291 } 292 } 293 294 CachingDef styleCaching = application.getStyleCaching(); 295 if (styleCaching != null) { 296 CachingDef oldStyleCaching = oldApplication.getStyleCaching(); 297 if (oldStyleCaching == null) { 298 oldStyleCaching = new CachingDef(); 299 oldApplication.setStyleCaching(oldStyleCaching); 300 } 301 if (styleCaching.getLifetime() != null) { 302 oldStyleCaching.setLifetime(styleCaching.getLifetime()); 303 } 304 } 305 306 Map<String, ViewDef> viewDefs = application.getViewDefs(); 307 if (!viewDefs.isEmpty()) { 308 Map<String, ViewDef> oldViewDefs = oldApplication.getViewDefs(); 309 for (Map.Entry<String, ViewDef> entry : viewDefs.entrySet()) { 310 oldViewDefs.put(entry.getKey(), entry.getValue()); 311 } 312 } 313 314 } 315 } 316 } 317 318 private void registerPerspectiveExtension(Extension extension) { 319 Object[] contribs = extension.getContributions(); 320 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 321 for (Object contrib : contribs) { 322 PerspectiveType perspective = (PerspectiveType) contrib; 323 if (!perspective.getName().matches("[a-z0-9_\\-]+")) { 324 log.error("Perspective names may only contain lowercase alphanumeric characters, underscores and hyphens "); 325 continue; 326 } 327 typeRegistry.register(perspective); 328 } 329 } 330 331 private void registerEngineExtension(Extension extension) { 332 Object[] contribs = extension.getContributions(); 333 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 334 for (Object contrib : contribs) { 335 EngineType engine = (EngineType) contrib; 336 if (!engine.getName().matches("[a-z0-9_\\-]+")) { 337 log.error("Rendering engine names may only contain lowercase alphanumeric characters, underscores and hyphens "); 338 continue; 339 } 340 typeRegistry.register(engine); 341 } 342 } 343 344 private void registerTemplateEngineExtension(Extension extension) { 345 Object[] contribs = extension.getContributions(); 346 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 347 for (Object contrib : contribs) { 348 TemplateEngineType engine = (TemplateEngineType) contrib; 349 if (!engine.getName().matches("[a-z0-9_\\-]+")) { 350 log.error("Template engine names may only contain lowercase alphanumeric characters, underscores and hyphens "); 351 continue; 352 } 353 typeRegistry.register(engine); 354 } 355 } 356 357 private void registerThemeExtension(Extension extension) { 358 Object[] contribs = extension.getContributions(); 359 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 360 361 for (Object contrib : contribs) { 362 ThemeDescriptor themeDescriptor = (ThemeDescriptor) contrib; 363 themeDescriptor.setContext(extension.getContext()); 364 themeDescriptor.setConfigured(true); 365 366 // register the theme descriptor even if the theme fails to load 367 typeRegistry.register(themeDescriptor); 368 } 369 } 370 371 private void registerTheme(ThemeDescriptor themeDescriptor) { 372 String src = themeDescriptor.getSrc(); 373 if (src == null) { 374 themeDescriptor.setLoadingFailed(true); 375 log.error("Could not load theme, source not set. "); 376 return; 377 } 378 try { 379 final boolean preload = true; 380 ThemeParser.registerTheme(themeDescriptor, preload); 381 } catch (ThemeIOException e) { 382 log.error("Could not register theme: " + src + " " + e.getMessage()); 383 } 384 } 385 386 private void registerThemeSetExtension(Extension extension) { 387 Object[] contribs = extension.getContributions(); 388 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 389 390 for (Object contrib : contribs) { 391 ThemeSet themeSet = (ThemeSet) contrib; 392 String name = themeSet.getName(); 393 394 ThemeSet oldThemeSet = (ThemeSet) typeRegistry.lookup(TypeFamily.THEMESET, name); 395 396 if (oldThemeSet == null) { 397 typeRegistry.register(themeSet); 398 } else { 399 for (ThemeSetEntry theme : themeSet.getThemes()) { 400 String themeName = theme.getName(); 401 ThemeSetEntry oldTheme = oldThemeSet.getTheme(themeName); 402 if (oldTheme == null) { 403 oldTheme = new ThemeSetEntry(themeName); 404 oldThemeSet.setTheme(oldTheme); 405 } 406 for (String feature : theme.getFeatures()) { 407 oldThemeSet.addFeatureToTheme(themeName, feature); 408 } 409 } 410 411 } 412 } 413 } 414 415 private void registerCustomThemes() { 416 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 417 for (File file : ThemeManager.getCustomThemeFiles()) { 418 ThemeDescriptor themeDescriptor = new ThemeDescriptor(); 419 themeDescriptor.setConfigured(false); 420 421 String src = null; 422 try { 423 src = String.format("file://%s", file.getCanonicalPath()); 424 } catch (IOException e) { 425 themeDescriptor.setLoadingFailed(true); 426 log.error("Could not read theme file: " + src); 427 continue; 428 } 429 430 themeDescriptor.setSrc(src); 431 432 try { 433 final boolean preload = true; 434 ThemeParser.registerTheme(themeDescriptor, preload); 435 } catch (ThemeIOException e) { 436 log.error("Could not register theme: " + src + " " + e.getMessage()); 437 continue; 438 } 439 typeRegistry.register(themeDescriptor); 440 } 441 log.debug("Registered local themes"); 442 } 443 444 private void registerPresetExtension(Extension extension) { 445 Object[] contribs = extension.getContributions(); 446 RuntimeContext extensionContext = extension.getContext(); 447 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 448 for (Object contrib : contribs) { 449 if (contrib instanceof PaletteType) { 450 registerPalette((PaletteType) contrib, extensionContext); 451 } else { 452 typeRegistry.register((Type) contrib); 453 } 454 } 455 } 456 457 private void registerPalette(PaletteType palette, RuntimeContext extensionContext) { 458 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 459 String paletteName = palette.getName(); 460 String src = palette.getSrc(); 461 String category = palette.getCategory(); 462 URL url = null; 463 try { 464 url = new URL(src); 465 } catch (MalformedURLException e) { 466 url = extensionContext.getLocalResource(src); 467 if (url == null) { 468 url = extensionContext.getResource(src); 469 } 470 } 471 472 if (url != null) { 473 typeRegistry.register(palette); 474 Map<String, String> entries = PaletteParser.parse(url); 475 for (Map.Entry<String, String> entry : entries.entrySet()) { 476 PresetType preset = new PresetType(entry.getKey(), entry.getValue(), paletteName, category, "", ""); 477 typeRegistry.register(preset); 478 } 479 } 480 } 481 482 private void registerViewExtension(Extension extension) { 483 Object[] contribs = extension.getContributions(); 484 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 485 for (Object contrib : contribs) { 486 ViewType viewType = (ViewType) contrib; 487 String templateEngineName = viewType.getTemplateEngine(); 488 final String viewName = viewType.getViewName(); 489 final String viewTypeName = viewType.getTypeName(); 490 if (templateEngineName == null) { 491 final String defaultTemplateEngineName = ThemeManager.getDefaultTemplateEngineName(); 492 templateEngineName = defaultTemplateEngineName; 493 log.warn(String.format( 494 "Please set the 'template-engine' attribute on <view name=\"%s\" template-engine=\"...\"> (using default '%s')", 495 viewName, defaultTemplateEngineName)); 496 } else { 497 if (templateEngineName.contains(",")) { 498 log.warn(String.format( 499 "The 'template-engine' attribute in <view name=\"%s\" template-engine=\"...\"> may only contain a single template-engine name", 500 viewName, templateEngineName)); 501 templateEngineName = templateEngineName.split(",")[0]; 502 } 503 504 if (viewType.isMerge()) { 505 final ViewType oldViewType = (ViewType) typeRegistry.lookup(TypeFamily.VIEW, viewTypeName); 506 if (oldViewType != null) { 507 // merge resource properties 508 List<String> newResources = viewType.getResources(); 509 if (!newResources.isEmpty()) { 510 log.debug("Added resources " + newResources + " to THEME view: " + viewTypeName); 511 for (String resource : newResources) { 512 oldViewType.addResource(resource); 513 } 514 } 515 } 516 } else { 517 typeRegistry.register(viewType); 518 } 519 } 520 } 521 } 522 523 private void unregisterViewExtension(Extension extension) { 524 Object[] contribs = extension.getContributions(); 525 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 526 if (typeRegistry != null) { 527 for (Object contrib : contribs) { 528 typeRegistry.unregister((ViewType) contrib); 529 } 530 } 531 } 532 533 private void registerModelExtension(Extension extension) { 534 Object[] contribs = extension.getContributions(); 535 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 536 ThemeManager themeManager = (ThemeManager) getRegistry("themes"); 537 for (Object contrib : contribs) { 538 ModelType modelType = (ModelType) contrib; 539 final String modelTypeName = modelType.getTypeName(); 540 final ModelType oldModelType = (ModelType) typeRegistry.lookup(TypeFamily.MODEL, modelTypeName); 541 if (oldModelType != null) { 542 if (oldModelType.getClassName().equals(modelType.getClassName())) { 543 log.debug("Model type '" + modelTypeName + "' (" + oldModelType.getClassName() 544 + ") has already been registered."); 545 } else { 546 log.warn("Failed to reregister model type '" + modelTypeName + "' (" + oldModelType.getClassName() 547 + "). The new class " + modelType.getClassName() + " will be ignored."); 548 } 549 continue; 550 } 551 typeRegistry.register(modelType); 552 themeManager.registerModelByClassname(modelType); 553 } 554 } 555 556 private void unregisterModelExtension(Extension extension) { 557 Object[] contribs = extension.getContributions(); 558 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 559 ThemeManager themeManager = (ThemeManager) getRegistry("themes"); 560 if (typeRegistry != null) { 561 for (Object contrib : contribs) { 562 ModelType modelType = (ModelType) contrib; 563 themeManager.unregisterModelByClassname(modelType); 564 typeRegistry.unregister(modelType); 565 } 566 } 567 } 568 569 private void registerResourceExtension(Extension extension) { 570 Object[] contribs = extension.getContributions(); 571 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 572 RuntimeContext extensionContext = extension.getContext(); 573 for (Object contrib : contribs) { 574 if (contrib instanceof ResourceType) { 575 ResourceType resourceType = (ResourceType) contrib; 576 typeRegistry.register(resourceType); 577 } else if (contrib instanceof BankImport) { 578 BankImport bankImport = (BankImport) contrib; 579 String bankName = bankImport.getBankName(); 580 String collection = bankImport.getCollection(); 581 String srcFilePath = bankImport.getSrcFilePath(); 582 URL srcFileUrl = extensionContext.getLocalResource(srcFilePath); 583 if (srcFileUrl == null) { 584 srcFileUrl = extensionContext.getResource(srcFilePath); 585 } 586 if (srcFileUrl == null) { 587 log.error("Could not import bank resources: " + srcFilePath + " (resource not found)"); 588 continue; 589 } 590 try { 591 BankManager.importBankData(bankName, collection, srcFileUrl); 592 } catch (IOException e) { 593 log.error("Could not import bank resources: " + srcFilePath + " (" + e.getMessage() + ")"); 594 } 595 } 596 } 597 ThemeManager themeManager = (ThemeManager) getRegistry("themes"); 598 themeManager.updateResourceOrdering(); 599 } 600 601 private void unregisterResourceExtension(Extension extension) { 602 Object[] contribs = extension.getContributions(); 603 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 604 ThemeManager themeManager = (ThemeManager) getRegistry("themes"); 605 if (typeRegistry == null || themeManager == null) { 606 return; 607 } 608 RuntimeContext extensionContext = extension.getContext(); 609 for (Object contrib : contribs) { 610 if (contrib instanceof ResourceType) { 611 ResourceType resourceType = (ResourceType) contrib; 612 typeRegistry.unregister(resourceType); 613 themeManager.unregisterResourceOrdering(resourceType); 614 } else if (contrib instanceof BankImport) { 615 BankImport bankImport = (BankImport) contrib; 616 String bankName = bankImport.getBankName(); 617 String srcFilePath = bankImport.getSrcFilePath(); 618 // TODO 619 } 620 } 621 } 622 623 private void registerBank(Extension extension) { 624 Object[] contribs = extension.getContributions(); 625 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 626 for (Object contrib : contribs) { 627 if (contrib instanceof ResourceBank) { 628 ResourceBank resourceBank = (ResourceBank) contrib; 629 typeRegistry.register(resourceBank); 630 } 631 } 632 } 633 634 private void unregisterBank(Extension extension) { 635 Object[] contribs = extension.getContributions(); 636 TypeRegistry typeRegistry = (TypeRegistry) getRegistry("types"); 637 if (typeRegistry == null) { 638 return; 639 } 640 for (Object contrib : contribs) { 641 if (contrib instanceof ResourceBank) { 642 ResourceBank resourceBank = (ResourceBank) contrib; 643 typeRegistry.unregister(resourceBank); 644 } 645 } 646 } 647 648}