public ObjectSpecification introspectIfRequired(final ObjectSpecification spec) {
    final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract) spec;
    final IntrospectionState introspectionState = specSpi.getIntrospectionState();

    if (introspectionState == IntrospectionState.NOT_INTROSPECTED) {
      specSpi.setIntrospectionState(IntrospectionState.BEING_INTROSPECTED);

      specSpi.introspectTypeHierarchyAndMembers();
      facetDecoratorSet.decorate(spec);
      specSpi.updateFromFacetValues();

      specSpi.setIntrospectionState(IntrospectionState.INTROSPECTED);
    } else if (introspectionState == IntrospectionState.BEING_INTROSPECTED) {
      // nothing to do

      specSpi.introspectTypeHierarchyAndMembers();
      facetDecoratorSet.decorate(spec);
      specSpi.updateFromFacetValues();

      specSpi.setIntrospectionState(IntrospectionState.INTROSPECTED);

    } else if (introspectionState == IntrospectionState.INTROSPECTED) {
      // nothing to do
    }
    return spec;
  }
  @Override
  public void debugData(final DebugBuilder debug) {
    facetDecoratorSet.debugData(debug);
    debug.appendln();

    debug.appendTitle("Specifications");
    final List<ObjectSpecification> specs = Lists.newArrayList(allSpecifications());
    Collections.sort(specs, ObjectSpecification.COMPARATOR_SHORT_IDENTIFIER_IGNORE_CASE);
    for (final ObjectSpecification spec : specs) {
      StringBuffer str = new StringBuffer();
      str.append(spec.isAbstract() ? "A" : ".");
      str.append(spec.isService() ? "S" : ".");
      str.append(ChoicesFacetUtils.hasChoices(spec) ? "B" : ".");
      str.append(spec.isParentedOrFreeCollection() ? "C" : ".");
      str.append(spec.isNotCollection() ? "O" : ".");
      str.append(spec.isParseable() ? "P" : ".");
      str.append(spec.isEncodeable() ? "E" : ".");
      str.append(spec.isValueOrIsParented() ? "A" : ".");

      final boolean hasIdentity =
          !(spec.isParentedOrFreeCollection() || spec.isParented() || spec.isValue());
      str.append(hasIdentity ? "I" : ".");
      str.append("  ");
      str.append(spec.getFullIdentifier());

      debug.appendPreformatted(spec.getShortIdentifier(), str.toString());
    }
  }
  @Override
  public void shutdown() {
    LOG.info("shutting down " + this);

    getCache().clear();
    facetDecoratorSet.shutdown();
  }
  /** For benefit of <tt>IsisMetaModel</tt>. */
  public ValidationFailures initAndValidate() {
    if (LOG.isDebugEnabled()) {
      LOG.debug("initialising " + this);
    }

    // default subcomponents
    if (runtimeContext == null) {
      runtimeContext = new RuntimeContextNoRuntime();
    }
    injectInto(runtimeContext);
    injectInto(specificationTraverser);
    injectInto(metaModelValidator);

    // wire subcomponents into each other
    runtimeContext.injectInto(facetProcessor);

    // initialize subcomponents
    facetDecoratorSet.init();
    classSubstitutor.init();
    collectionTypeRegistry.init();
    specificationTraverser.init();
    programmingModel.init();
    facetProcessor.init();
    metaModelValidator.init();

    primeCache();

    ValidationFailures validationFailures = new ValidationFailures();
    metaModelValidator.validate(validationFailures);
    return validationFailures;
  }
 protected Set<FacetDecorator> getFacetDecoratorSet() {
   return facetDecoratorSet.getFacetDecorators();
 }