001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.metamodel.specloader; 021 022import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg; 023import static org.hamcrest.CoreMatchers.is; 024import static org.hamcrest.CoreMatchers.notNullValue; 025 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032 033import com.google.common.base.Function; 034import com.google.common.collect.Lists; 035import com.google.common.collect.Maps; 036 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040import org.apache.isis.applib.DomainObjectContainer; 041import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider; 042import org.apache.isis.core.commons.components.ApplicationScopedComponent; 043import org.apache.isis.core.commons.config.IsisConfiguration; 044import org.apache.isis.core.commons.debug.DebugBuilder; 045import org.apache.isis.core.commons.debug.DebuggableWithTitle; 046import org.apache.isis.core.commons.ensure.Assert; 047import org.apache.isis.core.commons.exceptions.IsisException; 048import org.apache.isis.core.commons.lang.ClassUtil; 049import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 050import org.apache.isis.core.metamodel.adapter.ServicesProvider; 051import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 052import org.apache.isis.core.metamodel.deployment.DeploymentCategory; 053import org.apache.isis.core.metamodel.facetapi.Facet; 054import org.apache.isis.core.metamodel.facetdecorator.FacetDecorator; 055import org.apache.isis.core.metamodel.facetdecorator.FacetDecoratorSet; 056import org.apache.isis.core.metamodel.facets.object.bounded.ChoicesFacetUtils; 057import org.apache.isis.core.metamodel.facets.object.objecttype.ObjectSpecIdFacet; 058import org.apache.isis.core.metamodel.progmodel.ProgrammingModel; 059import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext; 060import org.apache.isis.core.metamodel.runtimecontext.RuntimeContextAware; 061import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector; 062import org.apache.isis.core.metamodel.runtimecontext.noruntime.RuntimeContextNoRuntime; 063import org.apache.isis.core.metamodel.spec.FreeStandingList; 064import org.apache.isis.core.metamodel.spec.ObjectInstantiator; 065import org.apache.isis.core.metamodel.spec.ObjectSpecId; 066import org.apache.isis.core.metamodel.spec.ObjectSpecification; 067import org.apache.isis.core.metamodel.spec.SpecificationContext; 068import org.apache.isis.core.metamodel.spec.SpecificationLoader; 069import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware; 070import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi; 071import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpiAware; 072import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext; 073import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor; 074import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry; 075import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistryDefault; 076import org.apache.isis.core.metamodel.specloader.facetprocessor.FacetProcessor; 077import org.apache.isis.core.metamodel.specloader.specimpl.CreateObjectContext; 078import org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilderContext; 079import org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionContext; 080import org.apache.isis.core.metamodel.specloader.specimpl.ObjectSpecificationAbstract; 081import org.apache.isis.core.metamodel.specloader.specimpl.ObjectSpecificationAbstract.IntrospectionState; 082import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault; 083import org.apache.isis.core.metamodel.specloader.specimpl.objectlist.ObjectSpecificationForFreeStandingList; 084import org.apache.isis.core.metamodel.specloader.traverser.SpecificationTraverser; 085import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator; 086import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures; 087import org.apache.isis.progmodels.dflt.ProgrammingModelFacetsJava5; 088 089/** 090 * Builds the meta-model. 091 * 092 * <p> 093 * The implementation provides for a degree of pluggability: 094 * <ul> 095 * <li>The most important plug-in point is {@link ProgrammingModel} that 096 * specifies the set of {@link Facet} that make up programming model. If not 097 * specified then defaults to {@link ProgrammingModelFacetsJava5} (which should 098 * be used as a starting point for your own customizations). 099 * <li>The only mandatory plug-in point is {@link ClassSubstitutor}, which 100 * allows the class to be loaded to be substituted if required. This is used in 101 * conjunction with some <tt>PersistenceMechanism</tt>s that do class 102 * enhancement. 103 * <li>The {@link CollectionTypeRegistry} specifies the types that should be 104 * considered as collections. If not specified then will 105 * {@link CollectionTypeRegistryDefault default}. (Note: this extension point 106 * has not been tested, so should be considered more of a "statement of 107 * intent" than actual API. Also, we may use annotations (similar to the 108 * way in which Values are specified) as an alternative mechanism). 109 * </ul> 110 * 111 * <p> 112 * In addition, the {@link RuntimeContext} can optionally be injected, but will 113 * default to {@link RuntimeContextNoRuntime} if not provided prior to 114 * {@link #init() initialization}. The purpose of {@link RuntimeContext} is to 115 * allow the metamodel to be used standalone, for example in a Maven plugin. The 116 * {@link RuntimeContextNoRuntime} implementation will through an exception for 117 * any methods (such as finding an {@link ObjectAdapter adapter}) because there 118 * is no runtime session. In the case of the metamodel being used by the 119 * framework (that is, when there <i>is</i> a runtime), then the framework 120 * injects an implementation of {@link RuntimeContext} that acts like a bridge 121 * to its <tt>IsisContext</tt>. 122 */ 123 124public final class ObjectReflectorDefault implements SpecificationLoaderSpi, ApplicationScopedComponent, RuntimeContextAware, DebuggableWithTitle { 125 126 private final static Logger LOG = LoggerFactory.getLogger(ObjectReflectorDefault.class); 127 128 /** 129 * Injected in the constructor. 130 */ 131 private final IsisConfiguration configuration; 132 /** 133 * Injected in the constructor. 134 */ 135 private final ClassSubstitutor classSubstitutor; 136 /** 137 * Injected in the constructor. 138 */ 139 private final CollectionTypeRegistry collectionTypeRegistry; 140 /** 141 * Injected in the constructor. 142 */ 143 private final ProgrammingModel programmingModel; 144 145 /** 146 * Defaulted in the constructor. 147 */ 148 private final FacetProcessor facetProcessor; 149 150 /** 151 * Defaulted in the constructor, so can be added to via 152 * {@link #setFacetDecorators(FacetDecoratorSet)} or 153 * {@link #addFacetDecorator(FacetDecorator)}. 154 * 155 * <p> 156 * {@link FacetDecorator}s must be added prior to {@link #init() 157 * initialization.} 158 */ 159 private final FacetDecoratorSet facetDecoratorSet; 160 161 /** 162 * Can optionally be injected, but will default (to 163 * {@link RuntimeContextNoRuntime}) otherwise. 164 * 165 * <p> 166 * Should be injected when used by framework, but will default to a no-op 167 * implementation if the metamodel is being used standalone (eg for a 168 * code-generator). 169 */ 170 private RuntimeContext runtimeContext; 171 172 private final SpecificationTraverser specificationTraverser; 173 174 /** 175 * @see #setContainer(DomainObjectContainer) 176 */ 177 private DomainObjectContainer container; 178 /** 179 * @see #setServices(List) 180 */ 181 private List<Object> services; 182 183 private final MetaModelValidator metaModelValidator; 184 private final SpecificationCacheDefault cache = new SpecificationCacheDefault(); 185 private final ServiceInitializer serviceInitializer = new ServiceInitializer(); 186 187 private boolean initialized = false; 188 189 190 // ///////////////////////////////////////////////////////////// 191 // Constructor 192 // ///////////////////////////////////////////////////////////// 193 194 public ObjectReflectorDefault( 195 final IsisConfiguration configuration, 196 final ClassSubstitutor classSubstitutor, 197 final CollectionTypeRegistry collectionTypeRegistry, 198 final SpecificationTraverser specificationTraverser, 199 final ProgrammingModel programmingModel, 200 final Set<FacetDecorator> facetDecorators, 201 final MetaModelValidator metaModelValidator) { 202 203 ensureThatArg(configuration, is(notNullValue())); 204 ensureThatArg(classSubstitutor, is(notNullValue())); 205 ensureThatArg(collectionTypeRegistry, is(notNullValue())); 206 ensureThatArg(specificationTraverser, is(notNullValue())); 207 ensureThatArg(programmingModel, is(notNullValue())); 208 ensureThatArg(facetDecorators, is(notNullValue())); 209 ensureThatArg(metaModelValidator, is(notNullValue())); 210 211 this.configuration = configuration; 212 this.classSubstitutor = classSubstitutor; 213 this.collectionTypeRegistry = collectionTypeRegistry; 214 this.programmingModel = programmingModel; 215 this.specificationTraverser = specificationTraverser; 216 217 this.facetDecoratorSet = new FacetDecoratorSet(); 218 for (final FacetDecorator facetDecorator : facetDecorators) { 219 this.facetDecoratorSet.add(facetDecorator); 220 } 221 222 this.metaModelValidator = metaModelValidator; 223 this.facetProcessor = new FacetProcessor(configuration, collectionTypeRegistry, programmingModel); 224 } 225 226 @Override 227 protected void finalize() throws Throwable { 228 super.finalize(); 229 LOG.info("finalizing reflector factory " + this); 230 } 231 232 // ///////////////////////////////////////////////////////////// 233 // init, shutdown 234 // ///////////////////////////////////////////////////////////// 235 236 /** 237 * Initializes and wires up, and primes the cache based on any service 238 * classes that may have been {@link #setServices(List) injected}. 239 */ 240 @Override 241 public void init() { 242 243 ValidationFailures validationFailures = initAndValidate(); 244 245 validationFailures.assertNone(); 246 247 cacheBySpecId(); 248 249 initialized = true; 250 } 251 252 @Override 253 public boolean isInitialized() { 254 return initialized; 255 } 256 257 /** 258 * For benefit of <tt>IsisMetaModel</tt>. 259 */ 260 public ValidationFailures initAndValidate() { 261 if (LOG.isDebugEnabled()) { 262 LOG.debug("initialising " + this); 263 } 264 265 // default subcomponents 266 if (runtimeContext == null) { 267 runtimeContext = new RuntimeContextNoRuntime(); 268 } 269 injectInto(runtimeContext); 270 injectInto(specificationTraverser); 271 injectInto(metaModelValidator); 272 273 // wire subcomponents into each other 274 runtimeContext.injectInto(facetProcessor); 275 276 // initialize subcomponents 277 facetDecoratorSet.init(); 278 classSubstitutor.init(); 279 collectionTypeRegistry.init(); 280 specificationTraverser.init(); 281 programmingModel.init(); 282 facetProcessor.init(); 283 metaModelValidator.init(); 284 285 primeCache(); 286 287 ValidationFailures validationFailures = new ValidationFailures(); 288 metaModelValidator.validate(validationFailures); 289 return validationFailures; 290 } 291 292 private void cacheBySpecId() { 293 final Map<ObjectSpecId, ObjectSpecification> specById = Maps.newHashMap(); 294 for (final ObjectSpecification objSpec : allSpecifications()) { 295 final ObjectSpecId objectSpecId = objSpec.getSpecId(); 296 if (objectSpecId == null) { 297 continue; 298 } 299 specById.put(objectSpecId, objSpec); 300 } 301 302 getCache().setCacheBySpecId(specById); 303 } 304 305 /** 306 * load the service specifications and then, using the 307 * {@link #getSpecificationTraverser() traverser}, keep loading all 308 * referenced specifications until we can find no more. 309 */ 310 private void primeCache() { 311 for (final Class<?> serviceClass : getServiceClasses()) { 312 internalLoadSpecification(serviceClass); 313 } 314 loadAllSpecifications(); 315 } 316 317 private void loadAllSpecifications() { 318 List<Class<?>> newlyDiscoveredClasses = newlyDiscoveredClasses(); 319 320 while (newlyDiscoveredClasses.size() > 0) { 321 for (final Class<?> newClass : newlyDiscoveredClasses) { 322 internalLoadSpecification(newClass); 323 } 324 newlyDiscoveredClasses = newlyDiscoveredClasses(); 325 } 326 } 327 328 private List<Class<?>> newlyDiscoveredClasses() { 329 final List<Class<?>> newlyDiscoveredClasses = new ArrayList<Class<?>>(); 330 331 final Collection<ObjectSpecification> noSpecs = allSpecifications(); 332 try { 333 for (final ObjectSpecification noSpec : noSpecs) { 334 getSpecificationTraverser().traverseReferencedClasses(noSpec, newlyDiscoveredClasses); 335 } 336 } catch (final ClassNotFoundException ex) { 337 throw new IsisException(ex); 338 } 339 return newlyDiscoveredClasses; 340 } 341 342 343 344 @Override 345 public void shutdown() { 346 LOG.info("shutting down " + this); 347 348 initialized = false; 349 350 getCache().clear(); 351 facetDecoratorSet.shutdown(); 352 } 353 354 355 @Override 356 public void invalidateCacheFor(Object domainObject) { 357 invalidateCache(domainObject.getClass()); 358 } 359 360 @Override 361 public void invalidateCache(final Class<?> cls) { 362 363 if(!getCache().isInitialized()) { 364 // could be called by JRebel plugin, before we are up-and-running 365 // just ignore. 366 return; 367 } 368 final Class<?> substitutedType = getClassSubstitutor().getClass(cls); 369 370 if(substitutedType.isAnonymousClass()) { 371 // JRebel plugin might call us... just ignore 'em. 372 return; 373 } 374 375 ObjectSpecification spec = loadSpecification(substitutedType); 376 while(spec != null) { 377 final Class<?> type = spec.getCorrespondingClass(); 378 getCache().remove(type.getName()); 379 if(spec.containsDoOpFacet(ObjectSpecIdFacet.class)) { 380 // umm. Some specs do not have an ObjectSpecIdFacet... 381 recache(spec); 382 } 383 spec = spec.superclass(); 384 } 385 } 386 387 private void recache(final ObjectSpecification newSpec) { 388 getCache().recache(newSpec); 389 } 390 391 392 393 // ///////////////////////////////////////////////////////////// 394 // install, load, allSpecifications, lookup 395 // ///////////////////////////////////////////////////////////// 396 397 /** 398 * API: Return the specification for the specified class of object. 399 */ 400 @Override 401 public final ObjectSpecification loadSpecification(final String className) { 402 ensureThatArg(className, is(notNullValue()), "specification class name must be specified"); 403 404 try { 405 final Class<?> cls = loadBuiltIn(className); 406 return internalLoadSpecification(cls); 407 } catch (final ClassNotFoundException e) { 408 final ObjectSpecification spec = getCache().get(className); 409 if (spec == null) { 410 throw new IsisException("No such class available: " + className); 411 } 412 return spec; 413 } 414 } 415 416 /** 417 * API: Return specification. 418 */ 419 @Override 420 public ObjectSpecification loadSpecification(final Class<?> type) { 421 final ObjectSpecification spec = internalLoadSpecification(type); 422 if(spec == null) { 423 return null; 424 } 425 if(getCache().isInitialized()) { 426 // umm. It turns out that anonymous inner classes (eg org.estatio.dom.WithTitleGetter$ToString$1) 427 // don't have an ObjectSpecId; hence the guard. 428 if(spec.containsDoOpFacet(ObjectSpecIdFacet.class)) { 429 ObjectSpecId specId = spec.getSpecId(); 430 if (getCache().getByObjectType(specId) == null) { 431 getCache().recache(spec); 432 } 433 } 434 } 435 return spec; 436 } 437 438 private ObjectSpecification internalLoadSpecification(final Class<?> type) { 439 final Class<?> substitutedType = getClassSubstitutor().getClass(type); 440 return substitutedType != null ? loadSpecificationForSubstitutedClass(substitutedType) : null; 441 } 442 443 private ObjectSpecification loadSpecificationForSubstitutedClass(final Class<?> type) { 444 Assert.assertNotNull(type); 445 final String typeName = type.getName(); 446 447 final SpecificationCacheDefault specificationCache = getCache(); 448 synchronized (specificationCache) { 449 final ObjectSpecification spec = specificationCache.get(typeName); 450 if (spec != null) { 451 return spec; 452 } 453 final ObjectSpecification specification = createSpecification(type); 454 if (specification == null) { 455 throw new IsisException("Failed to create specification for class " + typeName); 456 } 457 458 // put into the cache prior to introspecting, to prevent 459 // infinite loops 460 specificationCache.cache(typeName, specification); 461 462 introspectIfRequired(specification); 463 464 return specification; 465 } 466 } 467 468 /** 469 * Loads the specifications of the specified types except the one specified 470 * (to prevent an infinite loop). 471 */ 472 @Override 473 public boolean loadSpecifications(final List<Class<?>> typesToLoad, final Class<?> typeToIgnore) { 474 boolean anyLoadedAsNull = false; 475 for (final Class<?> typeToLoad : typesToLoad) { 476 if (typeToLoad != typeToIgnore) { 477 final ObjectSpecification noSpec = internalLoadSpecification(typeToLoad); 478 final boolean loadedAsNull = (noSpec == null); 479 anyLoadedAsNull = loadedAsNull || anyLoadedAsNull; 480 } 481 } 482 return anyLoadedAsNull; 483 } 484 485 /** 486 * Loads the specifications of the specified types. 487 */ 488 @Override 489 public boolean loadSpecifications(final List<Class<?>> typesToLoad) { 490 return loadSpecifications(typesToLoad, null); 491 } 492 493 /** 494 * Creates the appropriate type of {@link ObjectSpecification}. 495 */ 496 private ObjectSpecification createSpecification(final Class<?> cls) { 497 498 final AuthenticationSessionProvider authenticationSessionProvider = getRuntimeContext().getAuthenticationSessionProvider(); 499 final SpecificationLoader specificationLookup = getRuntimeContext().getSpecificationLoader(); 500 final ServicesProvider servicesProvider = getRuntimeContext().getServicesProvider(); 501 final ObjectInstantiator objectInstantiator = getRuntimeContext().getObjectInstantiator(); 502 503 // create contexts as inputs ... 504 final SpecificationContext specContext = new SpecificationContext(getDeploymentCategory(), authenticationSessionProvider, servicesProvider, objectInstantiator, specificationLookup, facetProcessor); 505 506 final AdapterManager adapterMap = getRuntimeContext().getAdapterManager(); 507 final ObjectMemberContext objectMemberContext = new ObjectMemberContext(getDeploymentCategory(), authenticationSessionProvider, specificationLookup, adapterMap, getRuntimeContext().getQuerySubmitter(), collectionTypeRegistry, servicesProvider); 508 509 // ... and create the specs 510 if (FreeStandingList.class.isAssignableFrom(cls)) { 511 return new ObjectSpecificationForFreeStandingList(specContext, objectMemberContext); 512 } else { 513 final SpecificationLoaderSpi specificationLoader = this; 514 final IntrospectionContext introspectionContext = new IntrospectionContext(getClassSubstitutor()); 515 final ServicesInjector dependencyInjector = getRuntimeContext().getDependencyInjector(); 516 final CreateObjectContext createObjectContext = new CreateObjectContext(adapterMap, dependencyInjector); 517 final FacetedMethodsBuilderContext facetedMethodsBuilderContext = new FacetedMethodsBuilderContext(specificationLoader, classSubstitutor, specificationTraverser, facetProcessor); 518 return new ObjectSpecificationDefault(cls, facetedMethodsBuilderContext, introspectionContext, specContext, objectMemberContext, createObjectContext); 519 } 520 } 521 522 private DeploymentCategory getDeploymentCategory() { 523 if(runtimeContext == null) { 524 throw new IllegalStateException("Runtime context has not been injected."); 525 } 526 return runtimeContext.getDeploymentCategory(); 527 } 528 529 private Class<?> loadBuiltIn(final String className) throws ClassNotFoundException { 530 final Class<?> builtIn = ClassUtil.getBuiltIn(className); 531 if (builtIn != null) { 532 return builtIn; 533 } 534 return Class.forName(className); 535 } 536 537 /** 538 * Return all the loaded specifications. 539 */ 540 @Override 541 public Collection<ObjectSpecification> allSpecifications() { 542 return getCache().allSpecifications(); 543 } 544 545 @Override 546 public boolean loaded(final Class<?> cls) { 547 return loaded(cls.getName()); 548 } 549 550 @Override 551 public boolean loaded(final String fullyQualifiedClassName) { 552 return getCache().get(fullyQualifiedClassName) != null; 553 } 554 555 public ObjectSpecification introspectIfRequired(final ObjectSpecification spec) { 556 final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract)spec; 557 final IntrospectionState introspectionState = specSpi.getIntrospectionState(); 558 559 if (introspectionState == IntrospectionState.NOT_INTROSPECTED) { 560 specSpi.setIntrospectionState(IntrospectionState.BEING_INTROSPECTED); 561 562 specSpi.introspectTypeHierarchyAndMembers(); 563 facetDecoratorSet.decorate(spec); 564 specSpi.updateFromFacetValues(); 565 566 specSpi.setIntrospectionState(IntrospectionState.INTROSPECTED); 567 } else if (introspectionState == IntrospectionState.BEING_INTROSPECTED) { 568 // nothing to do 569 570 specSpi.introspectTypeHierarchyAndMembers(); 571 facetDecoratorSet.decorate(spec); 572 specSpi.updateFromFacetValues(); 573 574 specSpi.setIntrospectionState(IntrospectionState.INTROSPECTED); 575 576 } else if (introspectionState == IntrospectionState.INTROSPECTED) { 577 // nothing to do 578 } 579 return spec; 580 } 581 582 @Override 583 public ObjectSpecification lookupBySpecId(ObjectSpecId objectSpecId) { 584 return getCache().getByObjectType(objectSpecId); 585 } 586 587 588 // //////////////////////////////////////////////////////////////////// 589 // injectInto 590 // //////////////////////////////////////////////////////////////////// 591 592 /** 593 * Injects self into candidate if required, and instructs its subcomponents 594 * to do so also. 595 */ 596 @Override 597 public void injectInto(final Object candidate) { 598 final Class<?> candidateClass = candidate.getClass(); 599 if (SpecificationLoaderSpiAware.class.isAssignableFrom(candidateClass)) { 600 final SpecificationLoaderSpiAware cast = SpecificationLoaderSpiAware.class.cast(candidate); 601 cast.setSpecificationLoaderSpi(this); 602 } 603 if (SpecificationLoaderAware.class.isAssignableFrom(candidateClass)) { 604 final SpecificationLoaderAware cast = SpecificationLoaderAware.class.cast(candidate); 605 cast.setSpecificationLookup(this); 606 } 607 608 getClassSubstitutor().injectInto(candidate); 609 getCollectionTypeRegistry().injectInto(candidate); 610 } 611 612 // ///////////////////////////////////////////////////////////// 613 // Debugging 614 // ///////////////////////////////////////////////////////////// 615 616 @Override 617 public void debugData(final DebugBuilder debug) { 618 facetDecoratorSet.debugData(debug); 619 debug.appendln(); 620 621 debug.appendTitle("Specifications"); 622 final List<ObjectSpecification> specs = Lists.newArrayList(allSpecifications()); 623 Collections.sort(specs, ObjectSpecification.COMPARATOR_SHORT_IDENTIFIER_IGNORE_CASE); 624 for (final ObjectSpecification spec : specs) { 625 StringBuffer str = new StringBuffer(); 626 str.append(spec.isAbstract() ? "A" : "."); 627 str.append(spec.isService() ? "S" : "."); 628 str.append(ChoicesFacetUtils.hasChoices(spec) ? "B" : "."); 629 str.append(spec.isParentedOrFreeCollection() ? "C" : "."); 630 str.append(spec.isNotCollection() ? "O" : "."); 631 str.append(spec.isParseable() ? "P" : "."); 632 str.append(spec.isEncodeable() ? "E" : "."); 633 str.append(spec.isValueOrIsParented() ? "A" : "."); 634 635 final boolean hasIdentity = !(spec.isParentedOrFreeCollection() || spec.isParented() || spec.isValue()); 636 str.append( hasIdentity ? "I" : "."); 637 str.append(" "); 638 str.append(spec.getFullIdentifier()); 639 640 debug.appendPreformatted(spec.getShortIdentifier(), str.toString()); 641 } 642 } 643 644 @Override 645 public String debugTitle() { 646 return "Reflector"; 647 } 648 649 // ///////////////////////////////////////////////////////////// 650 // Helpers (were previously injected, but no longer required) 651 // ///////////////////////////////////////////////////////////// 652 653 /** 654 * Provides access to the registered {@link Facet}s. 655 */ 656 public FacetProcessor getFacetProcessor() { 657 return facetProcessor; 658 } 659 660 private SpecificationCacheDefault getCache() { 661 return cache; 662 } 663 664 // //////////////////////////////////////////////////////////////////// 665 // Dependencies (injected by setter due to *Aware) 666 // //////////////////////////////////////////////////////////////////// 667 668 /** 669 * As per {@link #setRuntimeContext(RuntimeContext)}. 670 */ 671 public RuntimeContext getRuntimeContext() { 672 return runtimeContext; 673 } 674 675 /** 676 * Due to {@link RuntimeContextAware}. 677 */ 678 @Override 679 public void setRuntimeContext(final RuntimeContext runtimeContext) { 680 this.runtimeContext = runtimeContext; 681 } 682 683 // //////////////////////////////////////////////////////////////////// 684 // Dependencies (setters, optional) 685 // //////////////////////////////////////////////////////////////////// 686 687 public List<Class<?>> getServiceClasses() { 688 List<Class<?>> serviceClasses = Lists.transform(services, new Function<Object, Class<?>>(){ 689 public Class<?> apply(Object o) { 690 return o.getClass(); 691 } 692 }); 693 return Collections.unmodifiableList(serviceClasses); 694 } 695 696 private List<Object> getServices() { 697 return services; 698 } 699 700 @Override 701 public void setContainer(final DomainObjectContainer container) { 702 this.container = container; 703 } 704 705 @Override 706 public void setServices(final List<Object> services) { 707 this.services = services; 708 } 709 710 // //////////////////////////////////////////////////////////////////// 711 // Dependencies (injected from constructor) 712 // //////////////////////////////////////////////////////////////////// 713 714 protected IsisConfiguration getIsisConfiguration() { 715 return configuration; 716 } 717 718 protected CollectionTypeRegistry getCollectionTypeRegistry() { 719 return collectionTypeRegistry; 720 } 721 722 protected ClassSubstitutor getClassSubstitutor() { 723 return classSubstitutor; 724 } 725 726 protected SpecificationTraverser getSpecificationTraverser() { 727 return specificationTraverser; 728 } 729 730 protected ProgrammingModel getProgrammingModelFacets() { 731 return programmingModel; 732 } 733 734 protected MetaModelValidator getMetaModelValidator() { 735 return metaModelValidator; 736 } 737 738 protected Set<FacetDecorator> getFacetDecoratorSet() { 739 return facetDecoratorSet.getFacetDecorators(); 740 } 741 742 @Override 743 public void validateSpecifications(ValidationFailures validationFailures) { 744 final Map<ObjectSpecId, ObjectSpecification> specById = Maps.newHashMap(); 745 for (final ObjectSpecification objSpec : allSpecifications()) { 746 final ObjectSpecId objectSpecId = objSpec.getSpecId(); 747 if (objectSpecId == null) { 748 continue; 749 } 750 final ObjectSpecification existingSpec = specById.put(objectSpecId, objSpec); 751 if (existingSpec == null) { 752 continue; 753 } 754 validationFailures.add("Cannot have two entities with same object type (@ObjectType facet or equivalent) Value; " + "both %s and %s are annotated with value of ''%s''.", existingSpec.getFullIdentifier(), objSpec.getFullIdentifier(), objectSpecId); 755 } 756 } 757 758 759 760}