private Path<?> findPathOnParent(
      Path<?> parent, Class<?> targetType, JoinDescription joinDescription) {
    while (!targetType.equals(Object.class)) {
      // TODO more efficient cache key
      String cacheKey =
          parent.getClass().getName()
              + parent.toString()
              + targetType.getSimpleName()
              + joinDescription.getOriginalAlias().toString();
      Path cached = aliasCache.get(cacheKey);
      if (cached != null && !cached.equals(nullPath)) {
        // TODO test
        // TODO optimize inheritance cases
        return cached;
      }

      List<Path<?>> candidatePaths = new ArrayList<>();

      for (Field field : parent.getClass().getFields()) {
        Object candidate = getField(field, parent);

        testAliasCandidate(targetType, candidatePaths, candidate);
      }

      if (candidatePaths.isEmpty()) {
        for (Class child : ReflectionUtils.getSubclasses(parent.getType(), entityManager)) {
          Class<?> real;
          Constructor<?> constructor;
          try {
            real =
                Class.forName(
                    parent.getClass().getPackage().getName() + ".Q" + child.getSimpleName());
            constructor = real.getConstructor(String.class);
          } catch (Exception e) {
            throw new RuntimeException();
          }

          Object childInstance;
          try {
            childInstance = constructor.newInstance(parent.getMetadata().getElement());
          } catch (Exception e) {
            throw new RuntimeException();
          }
          for (Field field : real.getFields()) {

            Object candidate = getField(field, childInstance);

            testAliasCandidate(targetType, candidatePaths, candidate);
          }
        }
      }

      if (candidatePaths.isEmpty()) {
        // TODO may be exception?
        J.unrollChildrenJoins(Collections.singletonList(joinDescription))
            .forEach(j -> j.fetch(false));

        aliasCache.put(cacheKey, nullPath);
        targetType = targetType.getSuperclass();
      } else if (candidatePaths.size() == 1) {
        aliasCache.put(cacheKey, candidatePaths.get(0));
        return candidatePaths.get(0);
      } else {
        // Multiple associations on parent, try find by specified alias
        String targetFieldName = joinDescription.getOriginalAlias().toString();
        for (Path<?> candidatePath : candidatePaths) {
          if (targetFieldName.equals(candidatePath.getMetadata().getElement())) {
            aliasCache.put(cacheKey, candidatePath);
            return candidatePath;
          }
        }
        // TODO add candidates to exception
        throw new JoinerException(
            "Join with ambiguous alias : "
                + joinDescription
                + ". Multiple mappings found: "
                + candidatePaths);
      }
    }

    return null;
  }
Exemple #2
0
 @Override
 public boolean equals(Object o) {
   return pathMixin.equals(o);
 }