/** tests the PB.retrieveAllReferences() feature */
  public void testRetrieveAllReferences() {
    String name = "testRetrieveAllReferences_" + System.currentTimeMillis();

    // ensure there is an item to find
    Article tmpArticle = createArticle(name);
    ProductGroup pg = createProductGroup(name);
    tmpArticle.setProductGroup(pg);

    broker.beginTransaction();
    broker.store(pg);
    broker.store(tmpArticle);
    broker.commitTransaction();
    Identity tmpOID = broker.serviceIdentity().buildIdentity(tmpArticle);
    broker.clearCache();
    ObjectReferenceDescriptor ord = null;
    try {
      // switch to shallow retrieval
      ClassDescriptor cld = broker.getClassDescriptor(Article.class);
      ord = (ObjectReferenceDescriptor) cld.getObjectReferenceDescriptors().get(0);
      ord.setCascadeRetrieve(false);

      Article article = (Article) broker.getObjectByIdentity(tmpOID);
      assertNull("now reference should be null", article.getProductGroup());

      // now force loading:
      broker.retrieveAllReferences(article);
      assertNotNull("now reference should NOT be null", article.getProductGroup());

      // clean up cld
      ord.setCascadeRetrieve(true);
    } finally {
      // restore old value
      if (ord != null) ord.setCascadeRetrieve(true);
    }
  }
  /**
   * @see
   *     org.kuali.rice.krad.service.PersistenceStructureService#getBusinessObjectAttributeClass(java.lang.Class,
   *     java.lang.String)
   */
  public Class<? extends PersistableBusinessObjectExtension> getBusinessObjectAttributeClass(
      Class<? extends PersistableBusinessObject> clazz, String attributeName) {
    String baseAttributeName = attributeName;
    String subAttributeString = null;
    if (attributeName.contains(".")) {
      baseAttributeName = attributeName.substring(0, attributeName.indexOf('.'));
      subAttributeString = attributeName.substring(attributeName.indexOf('.') + 1);
    }

    // Legacy OJB
    Class attributeClassLegacy = null;
    ClassDescriptor classDescriptor = null;
    try {
      classDescriptor = this.getClassDescriptor(clazz);
    } catch (ClassNotPersistableException e) {
      LOG.warn("Class descriptor for " + clazz.getName() + "was not found");
    }

    ObjectReferenceDescriptor refDescriptor = null;
    if (classDescriptor != null) {
      refDescriptor = classDescriptor.getObjectReferenceDescriptorByName(baseAttributeName);
    }

    if (refDescriptor != null) {
      attributeClassLegacy = refDescriptor.getItemClass();
    }

    // recurse if necessary
    if (subAttributeString != null) {
      attributeClassLegacy =
          getBusinessObjectAttributeClass(attributeClassLegacy, subAttributeString);
    }

    return attributeClassLegacy;
  }
  public void testShallowAndDeepRetrieval() throws Exception {
    String name = "testShallowAndDeepRetrieval_" + System.currentTimeMillis();

    ObjectReferenceDescriptor ord = null;

    try {
      // prepare test, create article with ProductGroup
      Article tmpArticle = createArticle(name);
      ProductGroup pg = createProductGroup(name);
      tmpArticle.setProductGroup(pg);
      pg.add(tmpArticle);

      broker.beginTransaction();
      // in repository Article 1:1 refererence to PG hasn't enabled auto-update,
      // so first store the PG. PG has enabled auto-update and will store the
      // article automatic
      broker.store(pg);
      broker.commitTransaction();
      // after insert we can build the Article identity
      Identity tmpOID = broker.serviceIdentity().buildIdentity(tmpArticle);
      broker.clearCache();

      // switch to shallow retrieval
      ClassDescriptor cld = broker.getClassDescriptor(Article.class);
      ord = cld.getObjectReferenceDescriptorByName("productGroup");
      ord.setCascadeRetrieve(false);

      Article article = (Article) broker.getObjectByIdentity(tmpOID);
      assertNull("now reference should be null", article.getProductGroup());

      // now switch to deep retrieval
      ord.setCascadeRetrieve(true);
      // should work without setting cld
      // broker.setClassDescriptor(cld);
      broker.clearCache();
      article = (Article) broker.getObjectByIdentity(tmpOID);
      assertNotNull("now reference should NOT be null", article.getProductGroup());
    } finally {
      // restore old value
      if (ord != null) ord.setCascadeRetrieve(true);
    }
  }
  /** tests the PB.retrieveReference() feature */
  public void testRetrieveReference() throws Exception {
    String name = "testRetrieveReference_" + System.currentTimeMillis();

    // ensure there is an item to find
    Article tmpArticle = createArticle(name);
    ProductGroup pg = createProductGroup(name);
    tmpArticle.setProductGroup(pg);
    broker.beginTransaction();
    broker.store(pg);
    broker.store(tmpArticle);
    broker.commitTransaction();
    Identity tmpOID = broker.serviceIdentity().buildIdentity(tmpArticle);
    broker.clearCache();

    ObjectReferenceDescriptor ord = null;
    try {
      // switch to shallow retrieval
      ClassDescriptor cld = broker.getClassDescriptor(Article.class);
      // article only has one ord
      ord = cld.getObjectReferenceDescriptorByName("productGroup");
      ord.setCascadeRetrieve(false);

      Article article = (Article) broker.getObjectByIdentity(tmpOID);
      assertNull("now reference should be null", article.getProductGroup());

      // now force loading:
      broker.retrieveReference(article, "productGroup");
      assertNotNull("now reference should NOT be null", article.getProductGroup());

      // repair cld
      ord.setCascadeRetrieve(true);
      // should work without setting cld
      // broker.setClassDescriptor(cld);
    } finally {
      // restore old value
      if (ord != null) ord.setCascadeRetrieve(true);
    }
  }
  @Override
  public List<DataObjectRelationship> getRelationshipsTo(Class persistableClass)
      throws ClassNotPersistableException {
    if (!isPersistable(persistableClass)) {
      throw new ClassNotPersistableException(persistableClass.getName() + " is not persistable");
    }

    final List<DataObjectRelationship> relationships = new ArrayList<>();

    @SuppressWarnings("unchecked")
    final Set<Map.Entry<String, ClassDescriptor>> entries =
        (Set<Map.Entry<String, ClassDescriptor>>)
            getDescriptorRepository().getDescriptorTable().entrySet();
    for (Map.Entry<String, ClassDescriptor> entry : entries) {
      @SuppressWarnings("unchecked")
      final Collection<ObjectReferenceDescriptor> references =
          entry.getValue().getObjectReferenceDescriptors();
      if (references != null) {
        for (ObjectReferenceDescriptor reference : references) {
          if (reference.getItemClass().equals(persistableClass)) {
            final DataObjectRelationship relationship =
                new DataObjectRelationship(
                    entry.getValue().getClassOfObject(),
                    reference.getAttributeName(),
                    reference.getItemClass());
            final Map<String, String> pToC = new HashMap<>();
            for (int i = 0; i < reference.getForeignKeyFields().size(); i++) {
              final String fkField = (String) reference.getForeignKeyFields().get(i);
              pToC.put(
                  fkField,
                  getDescriptorRepository()
                      .getDescriptorFor(reference.getItemClass())
                      .getPkFields()[i]
                      .getAttributeName());
            }
            relationship.setParentToChildReferences(pToC);
            relationships.add(relationship);
          }
        }
      }
    }

    return relationships;
  }
  /**
   * gets the annotation but also adds an import in the process if a Convert annotation is required.
   */
  @Override
  protected NodeData getAnnotationNodes(
      String enclosingClass, String fieldName, String mappedClass) {
    final ObjectReferenceDescriptor ord =
        OjbUtil.findObjectReferenceDescriptor(mappedClass, fieldName, descriptorRepositories);
    if (ord != null) {
      final List<MemberValuePair> pairs = new ArrayList<MemberValuePair>();
      final Collection<ImportDeclaration> additionalImports = new ArrayList<ImportDeclaration>();

      final Collection<String> fks = ord.getForeignKeyFields();
      if (fks == null || fks.isEmpty()) {
        LOG.error(
            ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                + " field has a reference descriptor for "
                + fieldName
                + " but does not have any foreign keys configured");
        return null;
      }

      final Collection<String> pks =
          OjbUtil.getPrimaryKeyNames(mappedClass, descriptorRepositories);

      if (!(pks.size() == fks.size() && pks.containsAll(fks))) {
        final String className = ord.getItemClassName();
        if (StringUtils.isBlank(className)) {
          LOG.error(
              ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                  + " field has a reference descriptor for "
                  + fieldName
                  + " but does not class name attribute");
        } else {
          final String shortClassName = ClassUtils.getShortClassName(className);
          final String packageName = ClassUtils.getPackageName(className);
          pairs.add(new MemberValuePair("targetEntity", new NameExpr(shortClassName + ".class")));
          additionalImports.add(
              new ImportDeclaration(
                  new QualifiedNameExpr(new NameExpr(packageName), shortClassName), false, false));
        }

        final boolean proxy = ord.isLazy();
        if (proxy) {
          pairs.add(new MemberValuePair("fetch", new NameExpr("FetchType.LAZY")));
          additionalImports.add(
              new ImportDeclaration(
                  new QualifiedNameExpr(new NameExpr(PACKAGE), "FetchType"), false, false));
        }

        final boolean refresh = ord.isRefresh();
        if (refresh) {
          LOG.error(
              ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                  + " field has refresh set to "
                  + refresh
                  + ", unsupported conversion to @OneToOne attributes");
        }

        final List<Expression> cascadeTypes = new ArrayList<Expression>();
        final boolean autoRetrieve = ord.getCascadeRetrieve();
        if (autoRetrieve) {
          cascadeTypes.add(new NameExpr("CascadeType.REFRESH"));
        } else {
          // updated default logging - false would result no additional annotations
          if (LOG.isDebugEnabled()) {
            LOG.debug(
                ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                    + " field has auto-retrieve set to "
                    + autoRetrieve
                    + ", unsupported conversion to CascadeType");
          }
        }

        final int autoDelete = ord.getCascadingDelete();
        if (autoDelete == ObjectReferenceDescriptor.CASCADE_NONE) {
          // updated default logging - none would result no additional annotations
          if (LOG.isDebugEnabled()) {
            LOG.debug(
                ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                    + " field has auto-delete set to none, unsupported conversion to CascadeType");
          }
        } else if (autoDelete == ObjectReferenceDescriptor.CASCADE_LINK) {
          LOG.warn(
              ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                  + " field has auto-delete set to link, unsupported conversion to CascadeType");
        } else if (autoDelete == ObjectReferenceDescriptor.CASCADE_OBJECT) {
          cascadeTypes.add(new NameExpr("CascadeType.REMOVE"));
        } else {
          LOG.error(
              ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                  + " field has auto-delete set to an invalid value");
        }

        final int autoUpdate = ord.getCascadingStore();
        if (autoUpdate == ObjectReferenceDescriptor.CASCADE_NONE) {
          // updated default logging - none would result no additional annotations
          if (LOG.isDebugEnabled()) {
            LOG.debug(
                ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                    + " field has auto-update set to none, unsupported conversion to CascadeType");
          }
        } else if (autoUpdate == ObjectReferenceDescriptor.CASCADE_LINK) {
          LOG.warn(
              ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                  + " field has auto-update set to link, unsupported conversion to CascadeType");
        } else if (autoUpdate == ObjectReferenceDescriptor.CASCADE_OBJECT) {
          cascadeTypes.add(new NameExpr("CascadeType.PERSIST"));
        } else {
          LOG.error(
              ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass)
                  + " field has auto-update set to an invalid value");
        }

        if (!cascadeTypes.isEmpty()) {
          pairs.add(new MemberValuePair("cascade", new ArrayInitializerExpr(cascadeTypes)));
          additionalImports.add(
              new ImportDeclaration(
                  new QualifiedNameExpr(new NameExpr(PACKAGE), "CascadeType"), false, false));
        }

        return new NodeData(
            new NormalAnnotationExpr(new NameExpr(SIMPLE_NAME), pairs),
            new ImportDeclaration(
                new QualifiedNameExpr(new NameExpr(PACKAGE), SIMPLE_NAME), false, false),
            additionalImports);
      }
    }
    return null;
  }