protected void parseAttributeValue(
      String paramKey,
      String fileKey,
      Attribute attribute,
      Map<String, String[]> parameterMap,
      Map<String, MultipartFile> fileMap,
      TypedAttributeValue attributeValue)
      throws ParseException {
    AttributeType attrType = attribute.getType();
    if (attrType == AttributeType.TIME) {
      // parse out the time attribute if required...
      duckPunchTimeParameter("", attribute, parameterMap);
    }
    String attrValue = getParameter(parameterMap, paramKey);
    attrFile = fileMap.get(fileKey);

    if (AttributeType.MULTI_CHECKBOX.equals(attrType)
        || AttributeType.MULTI_SELECT.equals(attrType)
        || AttributeType.SINGLE_CHECKBOX.equals(attrType)) {

      // These types may have a null attrValue and still be valid.
      addOrUpdateAttribute = true;
      switch (attrType) {
        case MULTI_CHECKBOX:
          addOrUpdateAttribute = true;
          attributeValue.setMultiCheckboxValue(parameterMap.get(paramKey));
          break;
        case MULTI_SELECT:
          addOrUpdateAttribute = true;
          attributeValue.setMultiSelectValue(parameterMap.get(paramKey));
          break;
        case SINGLE_CHECKBOX:
          // Just clean up the input into "true" or "false"
          attributeValue.setBooleanValue(Boolean.valueOf(attrValue).toString());
          break;
        default:
          // Absolutely cannot get here.
          log.warn("Unknown Attribute Type: " + attribute.getType());
          break;
      }

    } else if (attrValue != null || attrFile != null) {
      addOrUpdateAttribute = true;
      attributeValue.setStringValue(attrValue);
      switch (attrType) {
        case TIME:
        case STRING:
        case STRING_AUTOCOMPLETE:
        case TEXT:
        case BARCODE:
        case REGEX:
        case HTML:
        case HTML_RAW:
        case HTML_NO_VALIDATION:
        case HTML_COMMENT:
        case HTML_HORIZONTAL_RULE:
          break;
        case STRING_WITH_VALID_VALUES:
          addOrUpdateAttribute = !attrValue.isEmpty();
          break;
        case INTEGER:
        case INTEGER_WITH_RANGE:
        case DECIMAL:
          addOrUpdateAttribute = !attrValue.isEmpty();
          if (addOrUpdateAttribute) {
            attributeValue.setNumericValue(new BigDecimal(attrValue));
          }
          break;
        case DATE:
          addOrUpdateAttribute = !attrValue.isEmpty();
          if (addOrUpdateAttribute) {
            attributeValue.setDateValue(dateFormat.parse(attrValue));
          }
          break;
        case IMAGE:
        case AUDIO:
        case VIDEO:
        case FILE:

          // attrValue is empty when a file is cleared or the client
          // does not have javascript enabled when uploading a file.
          // Without javascript, it is not possible to clear a file.

          // attrFile will always have size zero unless a file
          // is uploaded.

          // If there is already a file, but the
          // record is updated, without changing the file input,
          // addAttribute will be true but attrFile will
          // have size zero.
          addOrUpdateAttribute =
              (attrValue != null && !attrValue.isEmpty())
                  || (attrFile != null && attrFile.getSize() > 0);
          if (addOrUpdateAttribute && attrFile != null && attrFile.getSize() > 0) {
            attributeValue.setStringValue(attrFile.getOriginalFilename());
          } else {
            // Simplifies the need for users of this class to know about
            // zero sized files.
            attrFile = null;
          }
          break;
        case SPECIES:
          addOrUpdateAttribute = !attrValue.isEmpty();
          if (addOrUpdateAttribute) {
            // Regardless, attrValue should contain the verbatim name.
            String[] values = parameterMap.get(paramKey);
            if (values.length > 1 && values[1] != null && StringUtils.hasLength(values[1].trim())) {
              // use the id to retrieve a species.
              attributeValue.setStringValue(attrValue);
              IndicatorSpecies species = null;
              try {
                Integer speciesId = Integer.valueOf(values[1]);
                species = taxaDAO.getIndicatorSpecies(speciesId);
              } catch (NumberFormatException e) {
                log.warn("Could not parse string to int for species id", e);
              }
              attributeValue.setSpecies(species);
            } else {
              // use the name string to search.
              List<IndicatorSpecies> taxaList =
                  taxaDAO.getIndicatorSpeciesByNameSearchExact(attrValue);
              // validation has already been done by now so we can just grab the 0th index if it
              // exists.
              IndicatorSpecies species = null;
              if (!taxaList.isEmpty()) {
                species = taxaList.get(0);
              }
              attributeValue.setStringValue(attrValue);
              attributeValue.setSpecies(species);
            }
          }
          break;
        case CENSUS_METHOD_ROW:
        case CENSUS_METHOD_COL:
          // census method types do not have a string value, but should be added or updated
          addOrUpdateAttribute = true;
          attributeValue.setStringValue("");
          break;
        default:
          log.warn("Unknown Attribute Type: " + attrType);
          break;
      }
    } else if (AttributeType.isCensusMethodType(attrType)) {
      // census method types do not have a string value, but should be added or updated
      addOrUpdateAttribute = true;
    } else {
      addOrUpdateAttribute = false;
    }
  }