@Before
  public void setup() {
    survey = new Survey();
    survey.setName("my survey");
    for (RecordPropertyType type : RecordPropertyType.values()) {
      RecordProperty p = new RecordProperty(survey, type, mdDAO);
      p.setRequired(false);
    }

    List<Attribute> attrList = new ArrayList<Attribute>();

    attr = new Attribute();
    attr.setName("speciesattr");
    attr.setDescription("species attribute");
    attr.setTypeCode(AttributeType.SPECIES.getCode());
    attr.setRequired(true);

    attrList.add(attr);

    taxaDAO.save(attr);

    survey.setAttributes(attrList);

    surveyDAO.save(survey);
  }
  @Before
  public void setUp() throws Exception {
    taxonGroup = new TaxonGroup();
    taxonGroup.setName("Birds");
    taxonGroup = taxaDAO.save(taxonGroup);

    speciesA = new IndicatorSpecies();
    speciesA.setCommonName("Indicator Species A");
    speciesA.setScientificName("Indicator Species A");
    speciesA.setTaxonGroup(taxonGroup);
    speciesA = taxaDAO.save(speciesA);

    speciesB = new IndicatorSpecies();
    speciesB.setCommonName("Indicator Species B");
    speciesB.setScientificName("Indicator Species B");
    speciesB.setTaxonGroup(taxonGroup);
    speciesB = taxaDAO.save(speciesB);

    List<Attribute> attributeList = new ArrayList<Attribute>();
    Attribute attr;
    for (AttributeType attrType : AttributeType.values()) {
      for (AttributeScope scope :
          new AttributeScope[] {AttributeScope.RECORD, AttributeScope.SURVEY, null}) {

        attr = new Attribute();
        attr.setRequired(true);
        attr.setName(attrType.toString());
        attr.setTypeCode(attrType.getCode());
        attr.setScope(scope);
        attr.setTag(false);

        if (AttributeType.STRING_WITH_VALID_VALUES.equals(attrType)) {
          List<AttributeOption> optionList = new ArrayList<AttributeOption>();
          for (int i = 0; i < 4; i++) {
            AttributeOption opt = new AttributeOption();
            opt.setValue(String.format("Option %d", i));
            opt = taxaDAO.save(opt);
            optionList.add(opt);
          }
          attr.setOptions(optionList);
        }

        attr = taxaDAO.save(attr);
        attributeList.add(attr);
      }
    }

    survey = new Survey();
    survey.setName("SingleSiteMultiTaxaSurvey 1234");
    survey.setActive(true);
    survey.setStartDate(new Date());
    survey.setDescription("Single Site Multi Taxa Survey Description");
    Metadata md = survey.setFormRendererType(SurveyFormRendererType.SINGLE_SITE_MULTI_TAXA);
    metadataDAO.save(md);
    survey.setAttributes(attributeList);
    survey = surveyDAO.save(survey);
  }
  public void testSaveRecord(String intWithRangeValue) throws Exception {
    login("admin", "password", new String[] {Role.ADMIN});

    request.setMethod("POST");
    request.setRequestURI("/bdrs/user/singleSiteMultiTaxa.htm");

    DateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy");
    dateFormat.setLenient(false);

    GregorianCalendar cal = new GregorianCalendar();
    cal.set(2010, 10, 12, 15, 30);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    Date sightingDate = cal.getTime();

    Map<String, String> params = new HashMap<String, String>();
    params.put("surveyId", survey.getId().toString());
    params.put("latitude", "-36.879620605027");
    params.put("longitude", "126.650390625");
    params.put("date", dateFormat.format(sightingDate));
    params.put("time_hour", new Integer(cal.get(Calendar.HOUR_OF_DAY)).toString());
    params.put("time_minute", new Integer(cal.get(Calendar.MINUTE)).toString());
    params.put("notes", "This is a test record");
    params.put("sightingIndex", "2");

    Map<Attribute, Object> surveyScopeAttributeValueMapping = new HashMap<Attribute, Object>();
    Map<IndicatorSpecies, Map<Attribute, Object>> recordScopeAttributeValueMapping =
        new HashMap<IndicatorSpecies, Map<Attribute, Object>>(2);
    Map<Attribute, Object> attributeValueMapping;

    // We have 2 species set up so lets save them both
    int sightingIndex = 0;
    String surveyPrefix = "";
    for (IndicatorSpecies taxon : new IndicatorSpecies[] {speciesA, speciesB}) {
      params.put(
          String.format("%d_survey_species_search", sightingIndex), taxon.getScientificName());
      params.put(String.format("%d_species", sightingIndex), taxon.getId().toString());
      params.put(
          String.format("%d_number", sightingIndex), new Integer(sightingIndex + 21).toString());

      String recordPrefix = String.format("%d_", sightingIndex);
      String prefix;
      String key;
      String value; // The value in the post dict
      attributeValueMapping = new HashMap<Attribute, Object>();
      Map<Attribute, Object> valueMap;
      recordScopeAttributeValueMapping.put(taxon, attributeValueMapping);
      for (Attribute attr : survey.getAttributes()) {

        if (AttributeScope.SURVEY.equals(attr.getScope())) {
          prefix = surveyPrefix;
          valueMap = surveyScopeAttributeValueMapping;
        } else {
          prefix = recordPrefix;
          valueMap = attributeValueMapping;
        }

        key = String.format(AttributeParser.ATTRIBUTE_NAME_TEMPLATE, prefix, attr.getId());
        value = "";

        switch (attr.getType()) {
          case INTEGER:
            Integer val = new Integer(sightingIndex + 30);
            value = val.toString();
            valueMap.put(attr, val);
            break;
          case INTEGER_WITH_RANGE:
            valueMap.put(attr, intWithRangeValue);
            break;
          case DECIMAL:
            value = String.format("50.%d", sightingIndex);
            valueMap.put(attr, Double.parseDouble(value));
            break;
          case DATE:
            Date date = new Date(System.currentTimeMillis());
            value = dateFormat.format(date);
            // Reparsing the date strips out the hours, minutes and seconds
            valueMap.put(attr, dateFormat.parse(value));
            break;
          case STRING_AUTOCOMPLETE:
          case STRING:
            value = String.format("String %d", sightingIndex);
            valueMap.put(attr, value);
            break;
          case TEXT:
            value = String.format("Text %d", sightingIndex);
            valueMap.put(attr, value);
            break;
          case STRING_WITH_VALID_VALUES:
            value = attr.getOptions().get(sightingIndex).getValue();
            valueMap.put(attr, value);
            break;
          case FILE:
            String file_filename = String.format("attribute_%d", attr.getId());
            MockMultipartFile mockFileFile =
                new MockMultipartFile(key, file_filename, "audio/mpeg", file_filename.getBytes());
            ((MockMultipartHttpServletRequest) request).addFile(mockFileFile);
            valueMap.put(attr, mockFileFile);
            break;
          case IMAGE:
            String image_filename = String.format("attribute_%d", attr.getId());
            MockMultipartFile mockImageFile =
                new MockMultipartFile(key, image_filename, "image/png", image_filename.getBytes());
            ((MockMultipartHttpServletRequest) request).addFile(mockImageFile);
            valueMap.put(attr, mockImageFile);
            break;
          default:
            Assert.assertTrue("Unknown Attribute Type: " + attr.getType().toString(), false);
            break;
        }
        params.put(key, value);
      }
      sightingIndex += 1;
    }

    request.setParameters(params);
    ModelAndView mv = handle(request, response);
    Assert.assertEquals(2, recordDAO.countAllRecords().intValue());

    Assert.assertTrue(mv.getView() instanceof RedirectView);
    RedirectView redirect = (RedirectView) mv.getView();
    Assert.assertEquals(redirectionService.getMySightingsUrl(survey), redirect.getUrl());

    sightingIndex = 0;
    for (IndicatorSpecies taxon : new IndicatorSpecies[] {speciesA, speciesB}) {
      List<Record> records = recordDAO.getRecords(taxon);
      Assert.assertEquals(1, records.size());
      Record record = records.get(0);

      Assert.assertEquals(survey.getId(), record.getSurvey().getId());
      // Coordinates are truncates to 6 decimal points
      Assert.assertEquals(
          new Double(params.get("latitude")).doubleValue(),
          record.getPoint().getY(),
          Math.pow(10, -6));
      Assert.assertEquals(
          new Double(params.get("longitude")).doubleValue(),
          record.getPoint().getX(),
          Math.pow(10, -6));
      Assert.assertEquals(sightingDate, record.getWhen());
      Assert.assertEquals(sightingDate.getTime(), record.getTime().longValue());
      Assert.assertEquals(params.get("notes"), record.getNotes());

      Assert.assertEquals(taxon, record.getSpecies());
      Assert.assertEquals(sightingIndex + 21, record.getNumber().intValue());

      Map<Attribute, Object> attributeValueMap = recordScopeAttributeValueMapping.get(taxon);
      Object expected;
      for (TypedAttributeValue recAttr : record.getAttributes()) {
        if (AttributeScope.SURVEY.equals(recAttr.getAttribute().getScope())) {
          expected = surveyScopeAttributeValueMapping.get(recAttr.getAttribute());
        } else {
          expected = attributeValueMap.get(recAttr.getAttribute());
        }

        switch (recAttr.getAttribute().getType()) {
          case INTEGER:
          case INTEGER_WITH_RANGE:
            Assert.assertEquals(expected, recAttr.getNumericValue().intValue());
            break;
          case DECIMAL:
            Assert.assertEquals(expected, recAttr.getNumericValue().doubleValue());
            break;
          case DATE:
            Assert.assertEquals(expected, recAttr.getDateValue());
            break;
          case STRING_AUTOCOMPLETE:
          case STRING:
          case TEXT:
            Assert.assertEquals(expected, recAttr.getStringValue());
            break;
          case STRING_WITH_VALID_VALUES:
            Assert.assertEquals(expected, recAttr.getStringValue());
            break;
          case FILE:
          case IMAGE:
            String filename = ((MockMultipartFile) expected).getOriginalFilename();
            Assert.assertEquals(filename, recAttr.getStringValue());
            break;
          default:
            Assert.assertTrue(
                "Unknown Attribute Type: " + recAttr.getAttribute().getType().toString(), false);
            break;
        }
      }
      sightingIndex += 1;
    }

    // Test Save and Add Another
    request.setParameter("submitAndAddAnother", "submitAndAddAnother");
    mv = handle(request, response);
    Assert.assertEquals(4, recordDAO.countAllRecords().intValue());

    Assert.assertTrue(mv.getView() instanceof RedirectView);
    redirect = (RedirectView) mv.getView();
    Assert.assertEquals("/bdrs/user/surveyRenderRedirect.htm", redirect.getUrl());
  }
Beispiel #4
0
  @RolesAllowed({Role.USER, Role.POWERUSER, Role.SUPERVISOR, Role.ADMIN})
  @RequestMapping(value = "/bdrs/user/atlas.htm", method = RequestMethod.GET)
  public ModelAndView addRecord(
      HttpServletRequest request,
      HttpServletResponse response,
      @RequestParam(value = "surveyId", required = true) int surveyId,
      @RequestParam(value = "taxonSearch", required = false) String taxonSearch,
      @RequestParam(value = "recordId", required = false, defaultValue = "0") int recordId,
      @RequestParam(value = "guid", required = false) String guid) {

    Survey survey = surveyDAO.getSurvey(surveyId);
    Record record = recordDAO.getRecord(recordId);

    record = record == null ? new Record() : record;

    IndicatorSpecies species = null;

    if (guid != null && !guid.isEmpty()) {
      species = taxaDAO.getIndicatorSpeciesByGuid(guid);
    }
    if (species == null && taxonSearch != null && !taxonSearch.isEmpty()) {
      List<IndicatorSpecies> speciesList =
          surveyDAO.getSpeciesForSurveySearch(surveyId, taxonSearch);
      if (speciesList.isEmpty()) {
        species = null;
      } else if (speciesList.size() == 1) {
        species = speciesList.get(0);
      } else {
        log.warn(
            "Multiple species found for survey "
                + surveyId
                + " and taxon search \""
                + taxonSearch
                + "\". Using the first.");
        species = speciesList.get(0);
      }
    }
    if (species == null && record.getSpecies() != null) {
      species = record.getSpecies();
    }

    ModelAndView mv;
    if (species == null) {
      InputStreamReader reader = null;
      try {
        Preference p = preferenceDAO.getPreferenceByKey("ala.species.short.url");
        URL url;
        if (p != null) {
          url = new URL(p.getValue() + "/" + guid + ".json");
        } else {
          url =
              new URL(
                  "http://bie.ala.org.au/species/shortProfile/"
                      + guid
                      + ".json"); // fallback to the BIE
        }

        URLConnection conn = url.openConnection();
        reader = new InputStreamReader(conn.getInputStream());
        StringBuffer buff = new StringBuffer();
        int c;
        while ((c = reader.read()) != -1) {
          buff.append((char) c);
        }
        JSONObject ob = JSONObject.fromObject(buff.toString());
        IndicatorSpecies taxon = new IndicatorSpecies();

        log.debug("Found species information from the atlas : ");
        log.debug(buff.toString());
        // Scientific Name
        taxon.setScientificName(ob.getString("scientificName"));
        taxon.setAuthor(ob.getString("author"));
        taxon.setScientificNameAndAuthor(
            ob.getString("scientificName") + " " + ob.getString("scientificNameAuthorship"));
        taxon.setYear(ob.getString("year"));
        Metadata md = new Metadata();
        md.setKey(Metadata.SCIENTIFIC_NAME_SOURCE_DATA_ID);
        md.setValue(guid);
        metadataDAO.save(md);
        taxon.getMetadata().add(md);

        // Rank
        TaxonRank rank = TaxonRank.findByIdentifier(ob.getString("rank"));
        taxon.setTaxonRank(rank);

        // Common Name
        taxon.setCommonName(ob.getString("commonName"));
        md = new Metadata();
        md.setKey(Metadata.COMMON_NAME_SOURCE_DATA_ID);
        md.setValue(ob.getString("commonNameGUID"));
        metadataDAO.save(md);
        taxon.getMetadata().add(md);

        // Group
        String family = ob.getString("family");
        if (family != null) {
          TaxonGroup g = taxaDAO.getTaxonGroup(family);
          if (g == null) {
            g = taxaDAO.createTaxonGroup(family, false, false, false, false, false, true);
          }
          taxon.setTaxonGroup(g);
        } else {
          TaxonGroup g = taxaDAO.createTaxonGroup("Other", false, false, false, false, false, true);
          taxon.setTaxonGroup(g);
        }

        // Images.
        // Thumbnail
        if (ob.containsKey("thumbnail")) {
          SpeciesProfile sp = new SpeciesProfile();
          sp.setType(
              SpeciesProfile
                  .SPECIES_PROFILE_THUMBNAIL); // this is a 100x100 image, might resize for the
                                               // other ones.
          sp.setHeader("Thumbnail");
          sp.setDescription("Thumbnail for " + taxon.getScientificName());
          String filename = ob.getString("thumbnail");
          String ext = getExtension(filename);
          ManagedFile mf = new ManagedFile();
          mf.setContentType("");
          mf.setCredit("");
          mf.setLicense("");
          mf.setDescription(taxon.getScientificName() + " - " + taxon.getCommonName());
          mf.setFilename(mf.getUuid() + ext);
          mf = managedFileDAO.save(mf);
          fileService.createFile(
              mf.getClass(), mf.getId(), mf.getFilename(), downloadFile(new URL(filename)));
          mf.setContentType(fileService.getFile(mf, mf.getFilename()).getContentType());
          managedFileDAO.save(mf);
          sp.setContent(mf.getUuid());
          speciesProfileDAO.save(sp);
          taxon.getInfoItems().add(sp);

          // Now thumbnail the thumbnail...
          BufferedImage fortyXforty =
              imageService.resizeImage(
                  fileService.getFile(mf, mf.getFilename()).getInputStream(), 40, 40);
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          ImageIO.write(fortyXforty, "png", baos);
          baos.flush();
          byte[] data = baos.toByteArray();
          baos.close();

          sp = new SpeciesProfile();
          sp.setType(
              SpeciesProfile
                  .SPECIES_PROFILE_IMAGE_40x40); // this is a 100x100 image, might resize for the
                                                 // other ones.
          sp.setHeader("40x40 Thumbnail");
          sp.setDescription("40x40 Thumbnail for " + taxon.getScientificName());

          mf = new ManagedFile();
          mf.setContentType("");
          mf.setCredit("");
          mf.setLicense("");
          mf.setDescription(taxon.getScientificName() + " - " + taxon.getCommonName());
          mf.setFilename(mf.getUuid() + ".png");
          mf = managedFileDAO.save(mf);
          fileService.createFile(mf.getClass(), mf.getId(), mf.getFilename(), data);
          mf.setContentType(fileService.getFile(mf, mf.getFilename()).getContentType());
          managedFileDAO.save(mf);
          sp.setContent(mf.getUuid());
          speciesProfileDAO.save(sp);
          taxon.getInfoItems().add(sp);
        }

        // Main Image
        if (ob.containsKey("imageURL")) {
          SpeciesProfile sp = new SpeciesProfile();
          sp.setType(SpeciesProfile.SPECIES_PROFILE_IMAGE);
          sp.setHeader("Image");
          sp.setDescription("Image for " + taxon.getScientificName());
          String filename = ob.getString("imageURL");
          String ext = getExtension(filename);
          ManagedFile mf = new ManagedFile();
          mf.setContentType("");
          mf.setCredit("");
          mf.setLicense("");
          mf.setDescription(taxon.getScientificName() + " - " + taxon.getCommonName());
          mf.setFilename(mf.getUuid() + ext);
          mf = managedFileDAO.save(mf);
          fileService.createFile(
              mf.getClass(), mf.getId(), mf.getFilename(), downloadFile(new URL(filename)));
          mf.setContentType(fileService.getFile(mf, mf.getFilename()).getContentType());
          managedFileDAO.save(mf);
          sp.setContent(mf.getUuid());
          speciesProfileDAO.save(sp);
          taxon.getInfoItems().add(sp);
        }

        // Save the Taxon
        species = taxaDAO.save(taxon);
      } catch (IOException ioe) {
        log.error("Could not retrieve species profile from the Atlas", ioe);
        species = null;
      } finally {
        if (reader != null) {
          try {
            reader.close();
          } catch (IOException ioe) {
            log.error("Error closing stream from Atlas webservice, possible network error", ioe);
          }
        }
      }
    }

    if (species == null) {
      log.debug("Could not determine species, reverting to tracker form");
      // The atlas form relies upon a preconfigured species.
      // If we do not have one, fall back to the tracker form.
      mv = new ModelAndView(new RedirectView("tracker.htm"));
      mv.addAllObjects(request.getParameterMap());
      mv.addObject("surveyId", surveyId);
      return mv;
    } else {
      // Add all attribute form fields
      Map<String, FormField> formFieldMap = new HashMap<String, FormField>();

      // Add all property form fields
      for (String propertyName : RECORD_PROPERTY_NAMES) {
        formFieldMap.put(
            propertyName,
            formFieldFactory.createRecordFormField(
                survey, record, propertyName, species, Taxonomic.TAXONOMIC));
      }

      // Determine the file attribute to use for the form (if there is one)
      // Sort the list of survey attributes by weight so that we can
      // correctly select the first file attribute.
      List<Attribute> attributeList = survey.getAttributes();
      Collections.sort(attributeList, new ComparePersistentImplByWeight());

      // Retrieve the first file attribute and if present, the associated
      // record attribute.
      Attribute fileAttr = null;
      AttributeValue fileRecAttr = null;
      for (Attribute attr : attributeList) {
        if (fileAttr == null && AttributeType.FILE.equals(attr.getType())) {
          // Attribute found.
          fileAttr = attr;
          // Try to locate matching record attribute
          for (AttributeValue recAttr : record.getAttributes()) {
            if (fileRecAttr == null && fileAttr.equals(recAttr.getAttribute())) {
              fileRecAttr = recAttr;
            }
          }
        }
      }

      // Map all the existing file attributes to record attributes.
      Map<Attribute, AttributeValue> fileAttrToRecAttrMap =
          new HashMap<Attribute, AttributeValue>();

      FormField fileFormField =
          formFieldFactory.createRecordFormField(survey, record, fileAttr, fileRecAttr);

      Map<String, String> errorMap =
          (Map<String, String>) getRequestContext().getSessionAttribute("errorMap");
      getRequestContext().removeSessionAttribute("errorMap");
      Map<String, String> valueMap =
          (Map<String, String>) getRequestContext().getSessionAttribute("valueMap");
      getRequestContext().removeSessionAttribute("valueMap");

      Metadata predefinedLocationsMD = survey.getMetadataByKey(Metadata.PREDEFINED_LOCATIONS_ONLY);
      boolean predefinedLocationsOnly =
          predefinedLocationsMD != null && Boolean.parseBoolean(predefinedLocationsMD.getValue());

      Set<Location> locations = new TreeSet<Location>(new LocationNameComparator());
      locations.addAll(survey.getLocations());
      if (!predefinedLocationsOnly) {
        locations.addAll(locationDAO.getUserLocations(getRequestContext().getUser()));
      }

      Metadata defaultLocId =
          getRequestContext().getUser().getMetadataObj(Metadata.DEFAULT_LOCATION_ID);
      Location defaultLocation;
      if (defaultLocId == null) {
        defaultLocation = null;
      } else {
        int defaultLocPk = Integer.parseInt(defaultLocId.getValue());
        defaultLocation = locationDAO.getLocation(defaultLocPk);
      }

      mv = new ModelAndView("atlas");
      mv.addObject("record", record);
      mv.addObject("taxon", species);
      mv.addObject("survey", survey);
      mv.addObject("locations", locations);
      mv.addObject("formFieldMap", formFieldMap);
      mv.addObject("fileFormField", fileFormField);
      mv.addObject("preview", request.getParameter("preview") != null);
      mv.addObject("defaultLocation", defaultLocation);

      mv.addObject("errorMap", errorMap);
      mv.addObject("valueMap", valueMap);
    }

    return mv;
  }
  public boolean validate(
      RecordFormValidator validator,
      String paramKey,
      String fileKey,
      Attribute attribute,
      Map<String, String[]> parameterMap,
      Map<String, MultipartFile> fileMap) {

    ValidationType validationType;
    switch (attribute.getType()) {
      case STRING:
      case STRING_AUTOCOMPLETE:
      case TEXT:
      case STRING_WITH_VALID_VALUES:
        validationType =
            attribute.isRequired()
                ? ValidationType.REQUIRED_NONBLANK_STRING
                : ValidationType.STRING;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case MULTI_CHECKBOX:
      case MULTI_SELECT:
        validationType =
            attribute.isRequired()
                ? ValidationType.REQUIRED_NONBLANK_STRING
                : ValidationType.STRING;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case SINGLE_CHECKBOX:
        // No need to validate these values. They will always be valid no matter the input... even
        // null.
        // (An unchecked checkbox does not get POSTed)
        return true;
      case IMAGE:
      case AUDIO:
      case VIDEO:
      case FILE:
        validationType =
            attribute.isRequired()
                ? ValidationType.REQUIRED_NONBLANK_STRING
                : ValidationType.STRING;
        MultipartFile file = fileMap.get(fileKey);
        if (parameterMap.containsKey(paramKey)
            && getParameter(parameterMap, paramKey).isEmpty()
            && file != null) {
          // This bit of trickyness is to work around an issue where
          // with javascript turned off, the attribute value is not
          // populated even though the file is present. We are simply
          // fake the javascript component here by manually inserting
          // the filename into the attribute input and attempt a validation.
          parameterMap = new HashMap<String, String[]>(parameterMap);
          parameterMap.put(paramKey, new String[] {file.getOriginalFilename()});
        }
        boolean isValid = validator.validate(parameterMap, validationType, paramKey, attribute);
        return isValid;
      case REGEX:
        validationType =
            attribute.isRequired() ? ValidationType.REQUIRED_REGEX : ValidationType.REGEX;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case BARCODE:
        validationType =
            attribute.isRequired() ? ValidationType.REQUIRED_BARCODE : ValidationType.BARCODE;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case INTEGER:
        validationType =
            attribute.isRequired() ? ValidationType.REQUIRED_INTEGER : ValidationType.INTEGER;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case INTEGER_WITH_RANGE:
        validationType =
            attribute.isRequired()
                ? ValidationType.REQUIRED_INTEGER_RANGE
                : ValidationType.INTEGER_RANGE;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case DECIMAL:
        validationType =
            attribute.isRequired() ? ValidationType.REQUIRED_DOUBLE : ValidationType.DOUBLE;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case DATE:
        validationType =
            attribute.isRequired() ? ValidationType.REQUIRED_DATE : ValidationType.DATE;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case TIME:
        validationType =
            attribute.isRequired() ? ValidationType.REQUIRED_TIME : ValidationType.TIME;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case HTML:
      case HTML_RAW:
      case HTML_COMMENT:
      case HTML_HORIZONTAL_RULE:
        return validator.validate(parameterMap, ValidationType.HTML, paramKey, attribute);
      case HTML_NO_VALIDATION:
        // there is no validation, so it always returns true
        return true;
      case SPECIES:
        validationType =
            attribute.isRequired() ? ValidationType.REQUIRED_TAXON : ValidationType.TAXON;
        return validator.validate(parameterMap, validationType, paramKey, attribute);
      case CENSUS_METHOD_ROW:
      case CENSUS_METHOD_COL:
        // census method attributes are collections of attributes, need to
        // validate the collection of attributes for the record, however,
        // this cannot be done without more information that is given to this method
        return true;
      default:
        log.error("Unknown Attribute Type: " + attribute.getType());
        throw new IllegalArgumentException("Unknown Attribute Type: " + attribute.getType());
    }
  }
  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;
    }
  }