/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package org.opencastproject.index.service.catalog.adapter; import static com.entwinemedia.fn.data.json.Jsons.arr; import org.opencastproject.index.service.exception.ListProviderException; import org.opencastproject.index.service.resources.list.api.ListProvidersService; import org.opencastproject.index.service.resources.list.query.ResourceListQueryImpl; import org.opencastproject.metadata.dublincore.MetadataCollection; import org.opencastproject.metadata.dublincore.MetadataField; import org.opencastproject.metadata.dublincore.MetadataParsingException; import com.entwinemedia.fn.data.Opt; import com.entwinemedia.fn.data.json.JValue; import org.apache.commons.lang3.StringUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; /** * Abstract container for the metadata */ public abstract class AbstractMetadataCollection implements MetadataCollection { /** The logging facility */ private static final Logger logger = LoggerFactory.getLogger(AbstractMetadataCollection.class); /* Keys for the different properties of the metadata JSON Object */ protected static final String KEY_METADATA_ID = "id"; protected static final String KEY_METADATA_VALUE = "value"; /** The list containing all the metadata */ private List<MetadataField<?>> fieldsInOrder = new ArrayList<>(); private Map<String, MetadataField<?>> inputFields = new HashMap<>(); private Map<String, MetadataField<?>> outputFields = new HashMap<>(); @Override public JValue toJSON() { List<JValue> metadata = new ArrayList<>(); for (MetadataField<?> metadataField : getFields()) { metadata.add(metadataField.toJSON()); } return arr(metadata); } @Override public MetadataCollection fromJSON(String json) throws MetadataParsingException { if (StringUtils.isBlank(json)) throw new IllegalArgumentException("The JSON string must not be empty or null!"); JSONParser parser = new JSONParser(); JSONArray metadataJSON; try { metadataJSON = (JSONArray) parser.parse(json); } catch (ParseException e) { throw new MetadataParsingException("Not able to parse the given string as JSON event metadata.", e.getCause()); } ListIterator<JSONObject> listIterator = metadataJSON.listIterator(); while (listIterator.hasNext()) { JSONObject item = listIterator.next(); String fieldId = (String) item.get(KEY_METADATA_ID); MetadataField<?> target = null; if (fieldId == null) continue; Object value = item.get(KEY_METADATA_VALUE); if (value == null) continue; target = outputFields.get(fieldId); if (target == null) continue; target.fromJSON(value); } return this; } @Override public Map<String, MetadataField<?>> getInputFields() { return inputFields; } @Override public Map<String, MetadataField<?>> getOutputFields() { return outputFields; } @Override public void addField(MetadataField<?> metadata) { if (metadata == null) throw new IllegalArgumentException("The metadata must not be null."); addFieldInOrder(metadata); this.inputFields.put(metadata.getInputID(), metadata); this.outputFields.put(metadata.getOutputID(), metadata); } /** * Adds a field in ui order to the collection. If no order is specified it will be added to the end. * * @param metadata * The metadata to add to the collection. */ private void addFieldInOrder(MetadataField<?> metadata) { removeFieldIfExists(metadata); // Find all of the ordered or unordered elements. ArrayList<MetadataField<?>> orderedFields = new ArrayList<>(); ArrayList<MetadataField<?>> unorderedFields = new ArrayList<>(); for (MetadataField<?> field : fieldsInOrder) { if (field.getOrder().isSome()) { orderedFields.add(field); } else { unorderedFields.add(field); } } // Add the new field to either the ordered fields or the unordered fields. if (metadata.getOrder().isSome()) { orderedFields.add(metadata); } else { unorderedFields.add(metadata); } // Sort the ordered elements so that early entries don't push later entries to the right Collections.sort(orderedFields, new Comparator<MetadataField<?>>() { @Override public int compare(MetadataField<?> o1, MetadataField<?> o2) { return o1.getOrder().get() - o2.getOrder().get(); } }); // Add all the non-ordered elements to the collection fieldsInOrder = new ArrayList<>(unorderedFields); // Add all of the fields that have an index to their location starting at the lowest value. for (MetadataField<?> orderedField : orderedFields) { Integer index = orderedField.getOrder().get() < fieldsInOrder.size() ? orderedField.getOrder().get() : fieldsInOrder.size(); fieldsInOrder.add(index, orderedField); } } /** * Removes a {@link MetadataField} if it already exists in the ordered collection. * * @param metadata * The field to remove. */ private void removeFieldIfExists(MetadataField<?> metadata) { int index = -1; for (MetadataField<?> field : fieldsInOrder) { if (field.getInputID().equalsIgnoreCase(metadata.getInputID()) && field.getOutputID().equalsIgnoreCase(metadata.getOutputID())) { index = fieldsInOrder.indexOf(field); } } if (index >= 0) { fieldsInOrder.remove(index); } } @Override public void removeField(MetadataField<?> metadata) { if (metadata == null) throw new IllegalArgumentException("The metadata must not be null."); this.fieldsInOrder.remove(metadata); this.inputFields.remove(metadata.getInputID()); this.outputFields.remove(metadata.getOutputID()); } @Override public List<MetadataField<?>> getFields() { return this.fieldsInOrder; } @Override public void updateStringField(MetadataField<?> current, String value) { if (current.getValue().isSome() && !(current.getValue().get() instanceof String)) { throw new IllegalArgumentException("Unable to update a field to a different type than String with this method!"); } removeField(current); MetadataField<String> field = MetadataField.createTextMetadataField(current.getInputID(), Opt.some(current.getOutputID()), current.getLabel(), current.isReadOnly(), current.isRequired(), current.getCollection(), current.getCollectionID(), current.getOrder(), current.getNamespace()); field.setValue(value); addField(field); } @Override public void updateIterableStringField(MetadataField<?> current, Iterable<String> value) { if (!(current.getType().equals(MetadataField.Type.ITERABLE_TEXT))) { throw new IllegalArgumentException("Unable to update a field to a different type than String with this method!"); } removeField(current); MetadataField<Iterable<String>> field = MetadataField.createIterableStringMetadataField(current.getInputID(), Opt.some(current.getOutputID()), current.getLabel(), current.isReadOnly(), current.isRequired(), current.getCollection(), current.getCollectionID(), current.getOrder(), current.getNamespace()); field.setValue(value); addField(field); } /** * Get the values collection with the given name from the {@link ListProvidersService}. * * @param name * The target collection * @param listProviderService * The list provider service * @return A value collection with the given name wrapped in an {@link Opt}, or {@link Opt#none()} if no collection * has been found with this name. * @throws IllegalArgumentException * if the name or the listProviderService is null or the name blank. */ protected Opt<Map<String, String>> getCollection(String name, ListProvidersService listProviderService) { if (StringUtils.isBlank(name)) throw new IllegalArgumentException("The listName must not be null or empty!"); if (listProviderService == null) throw new IllegalArgumentException("The list provider must not be null!"); Opt<Map<String, String>> list; try { list = Opt.some(listProviderService.getList(name, new ResourceListQueryImpl(), null, true)); } catch (ListProviderException e) { logger.warn("Not able to find a value list with the name {}", name); list = Opt.none(); } return list; } @Override public boolean isUpdated() { for (MetadataField<?> field : fieldsInOrder) { if (field.isUpdated()) { return true; } } return false; } }