/**
   * If we have a work instance we have to check whether the instance to be indexed is contained in
   * any other indexed entities for a tenant.
   *
   * @param instance the instance to be indexed
   * @param workPlan the current work plan
   * @param currentRecursionContext the current {@link
   *     org.hibernate.search.engine.spi.ContainedInRecursionContext} object used to check the graph
   *     traversal
   * @param tenantIdentifier the identifier of the tenant or null, if there isn't one
   * @see #appendContainedInWorkForInstance(Object, WorkPlan, ContainedInRecursionContext)
   */
  public void appendContainedInWorkForInstance(
      Object instance,
      WorkPlan workPlan,
      ContainedInRecursionContext currentRecursionContext,
      String tenantIdentifier) {
    for (ContainedInMetadata containedInMetadata : typeMetadata.getContainedInMetadata()) {
      XMember member = containedInMetadata.getContainedInMember();
      Object unproxiedInstance = instanceInitializer.unproxy(instance);

      ContainedInRecursionContext recursionContext =
          updateContainedInRecursionContext(
              unproxiedInstance, containedInMetadata, currentRecursionContext);

      if (recursionContext.isTerminal()) {
        continue;
      }

      Object value = ReflectionHelper.getMemberValue(unproxiedInstance, member);

      if (value == null) {
        continue;
      }

      if (member.isArray()) {
        Object[] array = (Object[]) value;
        for (Object arrayValue : array) {
          processSingleContainedInInstance(
              workPlan, arrayValue, recursionContext, tenantIdentifier);
        }
      } else if (member.isCollection()) {
        Collection<?> collection = null;
        try {
          collection = getActualCollection(member, value);
          collection.size(); // load it
        } catch (Exception e) {
          if (e.getClass().getName().contains("org.hibernate.LazyInitializationException")) {
            /* A deleted entity not having its collection initialized
             * leads to a LIE because the collection is no longer attached to the session
             *
             * But that's ok as the collection update event has been processed before
             * or the fk would have been cleared and thus triggering the cleaning
             */
            collection = null;
          }
        }
        if (collection != null) {
          for (Object collectionValue : collection) {
            processSingleContainedInInstance(
                workPlan, collectionValue, recursionContext, tenantIdentifier);
          }
        }
      } else {
        processSingleContainedInInstance(workPlan, value, recursionContext, tenantIdentifier);
      }
    }
  }
 /**
  * @param annotatedElement an annotated element
  * @return a string which identifies the location/point the annotation was placed on. Something of
  *     the form package.[[className].[field|member]]
  */
 private String buildAnnotationDefinitionPoint(XAnnotatedElement annotatedElement) {
   if (annotatedElement instanceof XClass) {
     return ((XClass) annotatedElement).getName();
   } else if (annotatedElement instanceof XMember) {
     XMember member = (XMember) annotatedElement;
     return member.getType().getName() + '.' + member.getName();
   } else if (annotatedElement instanceof XPackage) {
     return ((XPackage) annotatedElement).getName();
   } else {
     throw new SearchException("Unknown XAnnotatedElement: " + annotatedElement);
   }
 }
 private ContainerType getContainerType(XMember member, ReflectionManager reflectionManager) {
   if (!member.isAnnotationPresent(IndexedEmbedded.class)) {
     return ContainerType.SINGLE;
   }
   if (member.isArray()) {
     return ContainerType.ARRAY;
   }
   Class<?> typeClass = reflectionManager.toClass(member.getType());
   if (Iterable.class.isAssignableFrom(typeClass)) {
     return ContainerType.ITERABLE;
   }
   if (member.isCollection() && Map.class.equals(member.getCollectionClass())) {
     return ContainerType.MAP;
   }
   // marked @IndexedEmbedded but not a container
   // => probably a @Field @IndexedEmbedded Foo foo;
   return ContainerType.SINGLE;
 }
 /**
  * A {@code XMember } instance treats a map as a collection as well in which case the map values
  * are returned as collection.
  *
  * @param member The member instance
  * @param value The value
  * @return The {@code value} cast to collection or in case of {@code value} being a map the map
  *     values as collection.
  */
 @SuppressWarnings("unchecked")
 private <T> Collection<T> getActualCollection(XMember member, Object value) {
   Collection<T> collection;
   if (Map.class.equals(member.getCollectionClass())) {
     collection = ((Map<?, T>) value).values();
   } else {
     collection = (Collection<T>) value;
   }
   return collection;
 }
 /**
  * Extract the field bridge from @Field.bridge or @FieldBridge. Return null if none is present.
  */
 private FieldBridge findExplicitFieldBridge(
     Field field, XMember member, ReflectionManager reflectionManager) {
   // TODO Should explicit FieldBridge also support the notion of container like numeric fields and
   // provider based fields?
   //     the main problem is that support for a bridge accepting a Map would break
   FieldBridge bridge = null;
   org.hibernate.search.annotations.FieldBridge bridgeAnn;
   // @Field bridge has priority over @FieldBridge
   if (field != null && void.class != field.bridge().impl()) {
     bridgeAnn = field.bridge();
   } else {
     bridgeAnn = member.getAnnotation(org.hibernate.search.annotations.FieldBridge.class);
   }
   if (bridgeAnn != null) {
     bridge = doExtractType(bridgeAnn, member, reflectionManager);
   }
   return bridge;
 }
 private FieldBridge doExtractType(
     org.hibernate.search.annotations.FieldBridge bridgeAnn,
     XMember member,
     ReflectionManager reflectionManager) {
   return doExtractType(bridgeAnn, member.getName(), reflectionManager.toClass(member.getType()));
 }
  public FieldBridge guessType(
      Field field,
      NumericField numericField,
      XMember member,
      ReflectionManager reflectionManager,
      ServiceManager serviceManager) {
    FieldBridge bridge = findExplicitFieldBridge(field, member, reflectionManager);
    if (bridge != null) {
      return bridge;
    }

    ExtendedBridgeProvider.ExtendedBridgeProviderContext context =
        new XMemberBridgeProviderContext(member, numericField, reflectionManager, serviceManager);
    ContainerType containerType = getContainerType(member, reflectionManager);

    // We do annotation based providers as Tike at least needs priority over
    // default providers because it might override the type for String
    // TODO: introduce the notion of bridge contributor annotations to cope with this in the future
    for (BridgeProvider provider : annotationBasedProviders) {
      bridge = getFieldBridgeFromBridgeProvider(provider, context, containerType);
      if (bridge != null) {
        return bridge;
      }
    }

    // walk through all regular bridges and if multiple match
    // raise an exception containing the conflicting bridges
    StringBuilder multipleMatchError = null;
    BridgeProvider initialMatchingBridgeProvider = null;
    for (BridgeProvider provider : regularProviders) {
      FieldBridge createdBridge =
          getFieldBridgeFromBridgeProvider(provider, context, containerType);
      if (createdBridge != null) {
        // oops we found a duplicate
        if (bridge != null) {
          // first duplicate, add the initial bridge
          if (multipleMatchError == null) {
            multipleMatchError =
                new StringBuilder("\n")
                    .append("FieldBridge: ")
                    .append(bridge)
                    .append(" - BridgeProvider: ")
                    .append(initialMatchingBridgeProvider.getClass());
          }
          multipleMatchError
              .append("\n")
              .append("FieldBridge: ")
              .append(createdBridge)
              .append(" - BridgeProvider: ")
              .append(provider.getClass());
        } else {
          bridge = createdBridge;
          initialMatchingBridgeProvider = provider;
        }
      }
    }
    if (multipleMatchError != null) {
      throw LOG.multipleMatchingFieldBridges(
          member, member.getType(), multipleMatchError.toString());
    }
    if (bridge != null) {
      return bridge;
    }

    throw LOG.unableToGuessFieldBridge(member.getType().getName(), member.getName());
  }