/** * Utility method for pattern-matching metadata elements. This method will return <code>true * </code> if the given schema, element, qualifier and language match the schema, element, * qualifier and language of the <code>DCValue</code> object passed in. Any or all of the element, * qualifier and language passed in can be the <code>Item.ANY</code> wildcard. * * @param schema the schema for the metadata field. <em>Must</em> match the <code>name</code> of * an existing metadata schema. * @param element the element to match, or <code>Item.ANY</code> * @param qualifier the qualifier to match, or <code>Item.ANY</code> * @param language the language to match, or <code>Item.ANY</code> * @param metadataValue the Dublin Core value * @return <code>true</code> if there is a match */ protected boolean match( String schema, String element, String qualifier, String language, MetadataValue metadataValue) { MetadataField metadataField = metadataValue.getMetadataField(); MetadataSchema metadataSchema = metadataField.getMetadataSchema(); // We will attempt to disprove a match - if we can't we have a match if (!element.equals(Item.ANY) && !element.equals(metadataField.getElement())) { // Elements do not match, no wildcard return false; } if (qualifier == null) { // Value must be unqualified if (metadataField.getQualifier() != null) { // Value is qualified, so no match return false; } } else if (!qualifier.equals(Item.ANY)) { // Not a wildcard, so qualifier must match exactly if (!qualifier.equals(metadataField.getQualifier())) { return false; } } if (language == null) { // Value must be null language to match if (metadataValue.getLanguage() != null) { // Value is qualified, so no match return false; } } else if (!language.equals(Item.ANY)) { // Not a wildcard, so language must match exactly if (!language.equals(metadataValue.getLanguage())) { return false; } } if (!schema.equals(Item.ANY)) { if (metadataSchema != null && !metadataSchema.getName().equals(schema)) { // The namespace doesn't match return false; } } // If we get this far, we have a match return true; }
@Override public void update(Context context, T dso) throws SQLException, AuthorizeException { if (dso.isMetadataModified()) { /* Update the order of the metadata values */ // A map created to store the latest place for each metadata field Map<MetadataField, Integer> fieldToLastPlace = new HashMap<>(); List<MetadataValue> metadataValues = dso.getMetadata(); for (MetadataValue metadataValue : metadataValues) { // Retrieve & store the place for each metadata value int mvPlace = getMetadataValuePlace(fieldToLastPlace, metadataValue); metadataValue.setPlace(mvPlace); } } }
@Override public void clearMetadata( Context context, T dso, String schema, String element, String qualifier, String lang) throws SQLException { // We will build a list of values NOT matching the values to clear Iterator<MetadataValue> metadata = dso.getMetadata().iterator(); while (metadata.hasNext()) { MetadataValue metadataValue = metadata.next(); if (match(schema, element, qualifier, lang, metadataValue)) { metadataValue.setDSpaceObject(null); metadata.remove(); // metadataValueService.delete(context, metadataValue); } } dso.setMetadataModified(); }
@Override public List<MetadataValue> getMetadata( T dso, String schema, String element, String qualifier, String lang, String authority) { List<MetadataValue> metadata = getMetadata(dso, schema, element, qualifier, lang); List<MetadataValue> result = new ArrayList<>(metadata); if (!authority.equals(Item.ANY)) { Iterator<MetadataValue> iterator = result.iterator(); while (iterator.hasNext()) { MetadataValue metadataValue = iterator.next(); if (!authority.equals(metadataValue.getAuthority())) { iterator.remove(); } } } return result; }
/** * Retrieve the place of the metadata value * * @param fieldToLastPlace the map containing the latest place of each metadata field * @param metadataValue the metadata value that needs to get a place * @return The new place for the metadata valu */ protected int getMetadataValuePlace( Map<MetadataField, Integer> fieldToLastPlace, MetadataValue metadataValue) { MetadataField metadataField = metadataValue.getMetadataField(); if (fieldToLastPlace.containsKey(metadataField)) { fieldToLastPlace.put(metadataField, fieldToLastPlace.get(metadataField) + 1); } else { // The metadata value place starts at 0 fieldToLastPlace.put(metadataField, 0); } return fieldToLastPlace.get(metadataField); }
@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; } }