/** * 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()); }