/**
   * Creates a searchable attribute value for the given property name out of the document XML
   *
   * @param propertyName the name of the property to return
   * @param businessObjectClass the class of the business object maintained
   * @param document the document XML
   * @return a generated SearchableAttributeValue, or null if a value could not be created
   */
  protected DocumentAttribute parseSearchableAttributeValueForPrimaryKey(
      String propertyName,
      Class<? extends BusinessObject> businessObjectClass,
      MaintenanceDocument document) {

    Maintainable maintainable = document.getNewMaintainableObject();
    PersistableBusinessObject bo = maintainable.getBusinessObject();

    final Object propertyValue = ObjectUtils.getPropertyValue(bo, propertyName);
    if (propertyValue == null) return null;

    final WorkflowAttributePropertyResolutionService propertyResolutionService =
        KNSServiceLocator.getWorkflowAttributePropertyResolutionService();
    DocumentAttribute value =
        propertyResolutionService.buildSearchableAttribute(
            businessObjectClass, propertyName, propertyValue);
    return value;
  }
  protected DocumentAttribute generateSearchableAttributeFromChange(Object changeToPersist) {
    List<String> primaryKeyNames =
        KRADServiceLocatorWeb.getLegacyDataAdapter()
            .listPrimaryKeyFieldNames(changeToPersist.getClass());

    for (Object primaryKeyNameAsObject : primaryKeyNames) {
      String primaryKeyName = (String) primaryKeyNameAsObject;
      Object value = ObjectUtils.getPropertyValue(changeToPersist, primaryKeyName);

      if (value != null) {
        final WorkflowAttributePropertyResolutionService propertyResolutionService =
            KNSServiceLocator.getWorkflowAttributePropertyResolutionService();
        DocumentAttribute saValue =
            propertyResolutionService.buildSearchableAttribute(
                (Class) changeToPersist.getClass(), primaryKeyName, value);
        return saValue;
      }
    }
    return null;
  }
  /**
   * Creates a list of search fields, one for each primary key of the maintained business object
   *
   * @param businessObjectClass the class of the maintained business object
   * @return a List of KEW search fields
   */
  protected List<Row> createFieldRowsForBusinessObject(
      Class<? extends BusinessObject> businessObjectClass) {
    List<Row> searchFields = new ArrayList<Row>();

    final List primaryKeyNamesAsObjects =
        KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(businessObjectClass);
    final BusinessObjectEntry boEntry =
        KRADServiceLocatorWeb.getDataDictionaryService()
            .getDataDictionary()
            .getBusinessObjectEntry(businessObjectClass.getName());
    final WorkflowAttributePropertyResolutionService propertyResolutionService =
        KNSServiceLocator.getWorkflowAttributePropertyResolutionService();
    for (Object primaryKeyNameAsObject : primaryKeyNamesAsObjects) {

      String attributeName = (String) primaryKeyNameAsObject;
      BusinessObject businessObject = null;
      try {
        businessObject = businessObjectClass.newInstance();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      Field searchField = FieldUtils.getPropertyField(businessObjectClass, attributeName, false);
      String dataType =
          propertyResolutionService.determineFieldDataType(businessObjectClass, attributeName);
      searchField.setFieldDataType(dataType);
      List<Field> fieldList = new ArrayList<Field>();

      List displayedFieldNames = new ArrayList();
      displayedFieldNames.add(attributeName);
      LookupUtils.setFieldQuickfinder(
          businessObject, attributeName, searchField, displayedFieldNames);

      fieldList.add(searchField);
      searchFields.add(new Row(fieldList));
    }

    return searchFields;
  }
  @Override
  public List<DocumentAttribute> extractDocumentAttributes(
      ExtensionDefinition extensionDefinition, DocumentWithContent documentWithContent) {
    List<DocumentAttribute> attributes = new ArrayList<DocumentAttribute>();

    String docId = documentWithContent.getDocument().getDocumentId();

    DocumentService docService = KRADServiceLocatorWeb.getDocumentService();
    Document doc = null;
    try {
      doc = docService.getByDocumentHeaderIdSessionless(docId);
    } catch (WorkflowException we) {
      LOG.error("Unable to retrieve document " + docId + " in getSearchStorageValues()", we);
    }

    String attributeValue = "";
    if (doc != null) {
      if (doc.getDocumentHeader() != null) {
        attributeValue = doc.getDocumentHeader().getDocumentDescription();
      } else {
        attributeValue = "null document header";
      }
    } else {
      attributeValue = "null document";
    }
    DocumentAttributeString attribute =
        DocumentAttributeFactory.createStringAttribute("documentDescription", attributeValue);
    attributes.add(attribute);

    attributeValue = "";
    if (doc != null) {
      if (doc.getDocumentHeader() != null) {
        attributeValue = doc.getDocumentHeader().getOrganizationDocumentNumber();
      } else {
        attributeValue = "null document header";
      }
    } else {
      attributeValue = "null document";
    }
    attribute =
        DocumentAttributeFactory.createStringAttribute(
            "organizationDocumentNumber", attributeValue);
    attributes.add(attribute);

    if (doc != null && doc instanceof MaintenanceDocument) {
      final Class<? extends BusinessObject> businessObjectClass =
          getBusinessObjectClass(documentWithContent.getDocument().getDocumentTypeName());
      if (businessObjectClass != null) {
        if (GlobalBusinessObject.class.isAssignableFrom(businessObjectClass)) {
          final GlobalBusinessObject globalBO =
              retrieveGlobalBusinessObject(docId, businessObjectClass);

          if (globalBO != null) {
            attributes.addAll(findAllDocumentAttributesForGlobalBusinessObject(globalBO));
          }
        } else {
          attributes.addAll(
              parsePrimaryKeyValuesFromDocument(businessObjectClass, (MaintenanceDocument) doc));
        }
      }
    }
    if (doc != null) {
      DocumentEntry docEntry =
          KRADServiceLocatorWeb.getDataDictionaryService()
              .getDataDictionary()
              .getDocumentEntry(documentWithContent.getDocument().getDocumentTypeName());
      if (docEntry != null) {
        WorkflowAttributes workflowAttributes = docEntry.getWorkflowAttributes();
        WorkflowAttributePropertyResolutionService waprs =
            KNSServiceLocator.getWorkflowAttributePropertyResolutionService();
        attributes.addAll(waprs.resolveSearchableAttributeValues(doc, workflowAttributes));
      } else {
        LOG.error(
            "Unable to find DD document entry for document type: "
                + documentWithContent.getDocument().getDocumentTypeName());
      }
    }
    return attributes;
  }
  protected List<Row> createFieldRowsForWorkflowAttributes(WorkflowAttributes attrs) {
    List<Row> searchFields = new ArrayList<Row>();

    List<SearchingTypeDefinition> searchingTypeDefinitions = attrs.getSearchingTypeDefinitions();
    final WorkflowAttributePropertyResolutionService propertyResolutionService =
        KNSServiceLocator.getWorkflowAttributePropertyResolutionService();
    for (SearchingTypeDefinition definition : searchingTypeDefinitions) {
      SearchingAttribute attr = definition.getSearchingAttribute();

      final String attributeName = attr.getAttributeName();
      final String businessObjectClassName = attr.getBusinessObjectClassName();
      Class boClass = null;
      Object businessObject = null;
      try {
        boClass = Class.forName(businessObjectClassName);
        businessObject = (Object) boClass.newInstance();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      Field searchField = FieldUtils.getPropertyField(boClass, attributeName, false);
      // prepend all document attribute field names with "documentAttribute."
      // searchField.setPropertyName(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX +
      // searchField.getPropertyName());
      searchField.setColumnVisible(attr.isShowAttributeInResultSet());

      // TODO this is a workaround to hide the Field from the search criteria.
      // This should be removed once hiding the entire Row is working
      if (!attr.isShowAttributeInSearchCriteria()) {
        searchField.setFieldType(Field.HIDDEN);
      }
      String fieldDataType =
          propertyResolutionService.determineFieldDataType(boClass, attributeName);
      if (fieldDataType.equals(DataDictionarySearchableAttribute.DATA_TYPE_BOOLEAN)) {
        fieldDataType = KewApiConstants.SearchableAttributeConstants.DATA_TYPE_STRING;
      }

      // Allow inline range searching on dates and numbers
      if (fieldDataType.equals(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_FLOAT)
          || fieldDataType.equals(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_LONG)
          || fieldDataType.equals(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_DATE)) {

        searchField.setAllowInlineRange(true);
      }
      searchField.setFieldDataType(fieldDataType);
      List displayedFieldNames = new ArrayList();
      displayedFieldNames.add(attributeName);
      LookupUtils.setFieldQuickfinder(
          businessObject, attributeName, searchField, displayedFieldNames);

      List<Field> fieldList = new ArrayList<Field>();
      fieldList.add(searchField);

      Row row = new Row(fieldList);
      if (!attr.isShowAttributeInSearchCriteria()) {
        row.setHidden(true);
      }
      searchFields.add(row);
    }

    return searchFields;
  }