@Override public void addMetadata( Context context, T dso, MetadataField metadataField, String lang, List<String> values, List<String> authorities, List<Integer> confidences) throws SQLException { boolean authorityControlled = metadataAuthorityService.isAuthorityControlled(metadataField); boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField); // We will not verify that they are valid entries in the registry // until update() is called. for (int i = 0; i < values.size(); i++) { MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField); metadataValue.setLanguage(lang == null ? null : lang.trim()); // Logic to set Authority and Confidence: // - normalize an empty string for authority to NULL. // - if authority key is present, use given confidence or NOVALUE if not given // - otherwise, preserve confidence if meaningful value was given since it may document a // failed authority lookup // - CF_UNSET signifies no authority nor meaningful confidence. // - it's possible to have empty authority & CF_ACCEPTED if e.g. user deletes authority key if (authorityControlled) { if (authorities != null && authorities.get(i) != null && authorities.get(i).length() > 0) { metadataValue.setAuthority(authorities.get(i)); metadataValue.setConfidence( confidences == null ? Choices.CF_NOVALUE : confidences.get(i)); } else { metadataValue.setAuthority(null); metadataValue.setConfidence(confidences == null ? Choices.CF_UNSET : confidences.get(i)); } // authority sanity check: if authority is required, was it supplied? // XXX FIXME? can't throw a "real" exception here without changing all the callers to expect // it, so use a runtime exception if (authorityRequired && (metadataValue.getAuthority() == null || metadataValue.getAuthority().length() == 0)) { throw new IllegalArgumentException( "The metadata field \"" + metadataField.toString() + "\" requires an authority key but none was provided. Value=\"" + values.get(i) + "\""); } } if (values.get(i) != null) { // remove control unicode char String temp = values.get(i).trim(); char[] dcvalue = temp.toCharArray(); for (int charPos = 0; charPos < dcvalue.length; charPos++) { if (Character.isISOControl(dcvalue[charPos]) && !String.valueOf(dcvalue[charPos]).equals("\u0009") && !String.valueOf(dcvalue[charPos]).equals("\n") && !String.valueOf(dcvalue[charPos]).equals("\r")) { dcvalue[charPos] = ' '; } } metadataValue.setValue(String.valueOf(dcvalue)); ; } else { metadataValue.setValue(null); } // An update here isn't needed, this is persited upon the merge of the owning object // metadataValueService.update(context, metadataValue); dso.addDetails(metadataField.toString()); } }
public void updateMetadata() throws SQLException, AuthorizeException { // Map counting number of values for each element/qualifier. // Keys are Strings: "element" or "element.qualifier" // Values are Integers indicating number of values written for a // element/qualifier Map<String, Integer> elementCount = new HashMap<String, Integer>(); modifiedMetadata = false; // Arrays to store the working information required int[] placeNum = new int[getMetadata().size()]; boolean[] storedDC = new boolean[getMetadata().size()]; MetadataField[] dcFields = new MetadataField[getMetadata().size()]; // Work out the place numbers for the in memory DC for (int dcIdx = 0; dcIdx < getMetadata().size(); dcIdx++) { DCValue dcv = getMetadata().get(dcIdx); // Work out the place number for ordering int current = 0; // Key into map is "element" or "element.qualifier" String key = dcv.element + ((dcv.qualifier == null) ? "" : ("." + dcv.qualifier)); Integer currentInteger = elementCount.get(key); if (currentInteger != null) { current = currentInteger.intValue(); } current++; elementCount.put(key, Integer.valueOf(current)); // Store the calculated place number, reset the stored flag, and cache the metadatafield placeNum[dcIdx] = current; storedDC[dcIdx] = false; dcFields[dcIdx] = getMetadataField(dcv); if (dcFields[dcIdx] == null) { // Bad DC field, log and throw exception log.warn("Invalid metadata field: [" + dcv.getField() + "] : [" + dcv.value + "]"); throw new SQLException("Invalid metadata field: [" + dcv.getField() + "]"); } } // Now the precalculations are done, iterate through the existing metadata // looking for matches TableRowIterator tri = retrieveMetadata(); if (tri != null) { try { while (tri.hasNext()) { TableRow tr = tri.next(); // Assume that we will remove this row, unless we get a match boolean removeRow = true; // Go through the in-memory metadata, unless we've already decided to keep this row for (int dcIdx = 0; dcIdx < getMetadata().size() && removeRow; dcIdx++) { // Only process if this metadata has not already been matched to something in the DB if (!storedDC[dcIdx]) { boolean matched = true; DCValue dcv = getMetadata().get(dcIdx); // Check the metadata field is the same if (matched && dcFields[dcIdx].getFieldID() != tr.getIntColumn("metadata_field_id")) { matched = false; } // Check the place is the same if (matched && placeNum[dcIdx] != tr.getIntColumn("place")) { matched = false; } // Check the text is the same if (matched) { String text = tr.getStringColumn("text_value"); if (dcv.value == null && text == null) { matched = true; } else if (dcv.value != null && dcv.value.equals(text)) { matched = true; } else { matched = false; } } // Check the language is the same if (matched) { String lang = tr.getStringColumn("text_lang"); if (dcv.language == null && lang == null) { matched = true; } else if (dcv.language != null && dcv.language.equals(lang)) { matched = true; } else { matched = false; } } // check that authority and confidence match if (matched) { String auth = tr.getStringColumn("authority"); int conf = tr.getIntColumn("confidence"); if (!((dcv.authority == null && auth == null) || (dcv.authority != null && auth != null && dcv.authority.equals(auth)) && dcv.confidence == conf)) { matched = false; } } // If the db record is identical to the in memory values if (matched) { // Flag that the metadata is already in the DB storedDC[dcIdx] = true; // Flag that we are not going to remove the row removeRow = false; } } } // If after processing all the metadata values, we didn't find a match // delete this row from the DB if (removeRow) { DatabaseManager.delete(ourContext, tr); modifiedMetadata = true; } } } finally { tri.close(); } } // Add missing in-memory DC for (int dcIdx = 0; dcIdx < getMetadata().size(); dcIdx++) { // Only write values that are not already in the db if (!storedDC[dcIdx]) { DCValue dcv = getMetadata().get(dcIdx); // Write DCValue MetadataValue metadata = new MetadataValue(); metadata.setResourceId(getID()); metadata.setResourceTypeId(getType()); metadata.setFieldId(dcFields[dcIdx].getFieldID()); metadata.setValue(dcv.value); metadata.setLanguage(dcv.language); metadata.setPlace(placeNum[dcIdx]); metadata.setAuthority(dcv.authority); metadata.setConfidence(dcv.confidence); metadata.create(ourContext); modifiedMetadata = true; } } if (modifiedMetadata) { ourContext.addEvent( new Event( Event.MODIFY_METADATA, getType(), getID(), getDetails(), getIdentifiers(ourContext))); modifiedMetadata = false; } }