/**
* 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.metadata.dublincore;
import static com.entwinemedia.fn.Stream.$;
import static org.opencastproject.metadata.dublincore.DublinCore.LANGUAGE_ANY;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_AUDIENCE;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_CONTRIBUTOR;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_CREATED;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_CREATOR;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_DESCRIPTION;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_EXTENT;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_IDENTIFIER;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_ISSUED;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_IS_PART_OF;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_LANGUAGE;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_LICENSE;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_PUBLISHER;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_RIGHTS_HOLDER;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_SOURCE;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_SPATIAL;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_TEMPORAL;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_TITLE;
import static org.opencastproject.metadata.dublincore.DublinCore.PROPERTY_TYPE;
import org.opencastproject.mediapackage.EName;
import org.opencastproject.metadata.dublincore.Temporal.Match;
import com.entwinemedia.fn.Fn;
import com.entwinemedia.fn.Stream;
import com.entwinemedia.fn.Unit;
import com.entwinemedia.fn.data.Opt;
import com.entwinemedia.fn.fns.Strings;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* {@link DublinCoreCatalog} wrapper to deal with DublinCore metadata according to the Opencast schema.
* <p>
* <h3>General behaviour</h3>
* <ul>
* <li>Set methods that take a string parameter only execute if the string is not blank.
* <li>Set methods that take a list of strings only execute if the list contains at least one non-blank string.
* <li>Set methods--if executed--replace the whole property with the given value/s.
* <li>Update methods only execute if the parameter is some non-blank string. If executed they
* behave like a set method and replace all exiting entries.
* <li>Add methods only execute if the parameter is some non-blank string.
* </ul>
*/
@ParametersAreNonnullByDefault
public abstract class OpencastDctermsDublinCore {
protected final DublinCoreCatalog dc;
private OpencastDctermsDublinCore(DublinCoreCatalog dc) {
this.dc = dc;
}
/** Return the wrapped catalog. */
public DublinCoreCatalog getCatalog() {
return dc;
}
/* ------------------------------------------------------------------------------------------------------------------ */
@Nonnull public List<String> getPublishers() {
return get(PROPERTY_PUBLISHER);
}
public void setPublishers(List<String> publishers) {
set(PROPERTY_PUBLISHER, publishers);
}
public void addPublisher(String publisher) {
add(PROPERTY_PUBLISHER, publisher);
}
public void removePublishers() {
dc.remove(PROPERTY_PUBLISHER);
}
/* ------------------------------------------------------------------------------------------------------------------ */
@Nonnull public List<String> getRightsHolders() {
return get(PROPERTY_RIGHTS_HOLDER);
}
public void setRightsHolders(List<String> rightsHolders) {
set(PROPERTY_RIGHTS_HOLDER, rightsHolders);
}
public void addRightsHolder(String rightsHolder) {
add(PROPERTY_RIGHTS_HOLDER, rightsHolder);
}
public void removeRightsHolders() {
dc.remove(PROPERTY_RIGHTS_HOLDER);
}
/* ------------------------------------------------------------------------------------------------------------------ */
@Nonnull public Opt<String> getLicense() {
return getFirst(PROPERTY_LICENSE);
}
public void setLicense(String license) {
set(PROPERTY_LICENSE, license);
}
public void removeLicense() {
dc.remove(PROPERTY_LICENSE);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_IDENTIFIER} property. */
@Nonnull public Opt<String> getDcIdentifier() {
return getFirst(PROPERTY_IDENTIFIER);
}
/** Set the {@link DublinCore#PROPERTY_IDENTIFIER} property. */
public void setDcIdentifier(String id) {
set(PROPERTY_IDENTIFIER, id);
}
/** Update the {@link DublinCore#PROPERTY_IDENTIFIER} property. */
public void updateDcIdentifier(Opt<String> id) {
update(PROPERTY_IDENTIFIER, id);
}
/** Remove the {@link DublinCore#PROPERTY_IDENTIFIER} property. */
public void removeDcIdentifier() {
dc.remove(PROPERTY_IDENTIFIER);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_TITLE} property. */
@Nonnull public Opt<String> getTitle() {
return getFirst(PROPERTY_TITLE);
}
/** Set the {@link DublinCore#PROPERTY_TITLE} property. */
public void setTitle(String title) {
set(PROPERTY_TITLE, title);
}
/** Update the {@link DublinCore#PROPERTY_TITLE} property. */
public void updateTitle(Opt<String> title) {
update(PROPERTY_TITLE, title);
}
/** Remove the {@link DublinCore#PROPERTY_TITLE} property. */
public void removeTitle() {
dc.remove(PROPERTY_TITLE);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_DESCRIPTION} property. */
@Nonnull public Opt<String> getDescription() {
return getFirst(PROPERTY_DESCRIPTION);
}
/** Set the {@link DublinCore#PROPERTY_DESCRIPTION} property. */
public void setDescription(String description) {
set(PROPERTY_DESCRIPTION, description);
}
/** Update the {@link DublinCore#PROPERTY_DESCRIPTION} property. */
public void updateDescription(Opt<String> description) {
update(PROPERTY_DESCRIPTION, description);
}
/** Remove the {@link DublinCore#PROPERTY_DESCRIPTION} property. */
public void removeDescription() {
dc.remove(PROPERTY_DESCRIPTION);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get all {@link DublinCore#PROPERTY_AUDIENCE} properties. */
@Nonnull public List<String> getAudiences() {
return get(PROPERTY_AUDIENCE);
}
/** Set multiple {@link DublinCore#PROPERTY_AUDIENCE} properties. */
public void setAudiences(List<String> audiences) {
set(PROPERTY_AUDIENCE, audiences);
}
/** Set the {@link DublinCore#PROPERTY_AUDIENCE} property. */
public void setAudience(String audience) {
set(PROPERTY_AUDIENCE, audience);
}
/** Add an {@link DublinCore#PROPERTY_AUDIENCE} property. */
public void addAudience(String audience) {
add(PROPERTY_AUDIENCE, audience);
}
/** Update the {@link DublinCore#PROPERTY_AUDIENCE} property. */
public void updateAudience(Opt<String> audience) {
update(PROPERTY_AUDIENCE, audience);
}
/** Remove all {@link DublinCore#PROPERTY_AUDIENCE} properties. */
public void removeAudiences() {
dc.remove(PROPERTY_AUDIENCE);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_CREATED} property. */
@Nonnull public Opt<Temporal> getCreated() {
return getFirstVal(PROPERTY_CREATED).map(OpencastMetadataCodec.decodeTemporal);
}
/** Set the {@link DublinCore#PROPERTY_CREATED} property. The date is encoded with a precision of {@link Precision#Day}. */
public void setCreated(Date date) {
setDate(PROPERTY_CREATED, date, Precision.Day);
}
/** Set the {@link DublinCore#PROPERTY_CREATED} property. The date is encoded with a precision of {@link Precision#Day}. */
public void setCreated(Temporal t) {
t.fold(new Match<Unit>() {
@Override public Unit period(DCMIPeriod period) {
setCreated(period.getStart());
return Unit.unit;
}
@Override public Unit instant(Date instant) {
setCreated(instant);
return Unit.unit;
}
@Override public Unit duration(long duration) {
return Unit.unit;
}
});
}
/** Remove the {@link DublinCore#PROPERTY_CREATED} property. */
public void removeCreated() {
dc.remove(PROPERTY_CREATED);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get all {@link DublinCore#PROPERTY_CREATOR} properties. */
@Nonnull public List<String> getCreators() {
return get(PROPERTY_CREATOR);
}
/** Set multiple {@link DublinCore#PROPERTY_CREATOR} properties. */
public void setCreators(List<String> creators) {
set(PROPERTY_CREATOR, creators);
}
/** Set the {@link DublinCore#PROPERTY_CREATOR} property. */
public void setCreator(String creator) {
set(PROPERTY_CREATOR, creator);
}
/** Add a {@link DublinCore#PROPERTY_CREATOR} property. */
public void addCreator(String name) {
add(PROPERTY_CREATOR, name);
}
/** Update the {@link DublinCore#PROPERTY_CREATOR} property. */
public void updateCreator(Opt<String> name) {
update(PROPERTY_CREATOR, name);
}
/** Remove all {@link DublinCore#PROPERTY_CREATOR} properties. */
public void removeCreators() {
dc.remove(PROPERTY_CREATOR);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_EXTENT} property. */
@Nonnull public Opt<Long> getExtent() {
return getFirst(PROPERTY_EXTENT).map(OpencastMetadataCodec.decodeDuration);
}
/** Set the {@link DublinCore#PROPERTY_EXTENT} property. */
public void setExtent(Long extent) {
dc.set(PROPERTY_EXTENT, OpencastMetadataCodec.encodeDuration(extent));
}
/** Remove the {@link DublinCore#PROPERTY_EXTENT} property. */
public void removeExtent() {
dc.remove(PROPERTY_EXTENT);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_ISSUED} property. */
@Nonnull public Opt<Date> getIssued() {
return getFirst(PROPERTY_ISSUED).map(OpencastMetadataCodec.decodeDate);
}
/** Set the {@link DublinCore#PROPERTY_ISSUED} property. */
public void setIssued(Date date) {
setDate(PROPERTY_ISSUED, date, Precision.Day);
}
/** Update the {@link DublinCore#PROPERTY_ISSUED} property. */
public void updateIssued(Opt<Date> date) {
updateDate(PROPERTY_ISSUED, date, Precision.Day);
}
/** Remove the {@link DublinCore#PROPERTY_ISSUED} property. */
public void removeIssued() {
dc.remove(PROPERTY_ISSUED);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_LANGUAGE} property. */
@Nonnull public Opt<String> getLanguage() {
return getFirst(PROPERTY_LANGUAGE);
}
/**
* Set the {@link DublinCore#PROPERTY_LANGUAGE} property.
* A 2- or 3-letter ISO code. 2-letter ISO codes are tried to convert into a 3-letter code.
* If this is not possible the provided string is used as is.
*/
public void setLanguage(String lang) {
if (StringUtils.isNotBlank(lang)) {
String doLang = lang;
if (lang.length() == 2) {
try {
doLang = new Locale(lang).getISO3Language();
} catch (MissingResourceException ignore) {
}
}
set(PROPERTY_LANGUAGE, doLang);
}
}
/** Remove the {@link DublinCore#PROPERTY_LANGUAGE} property. */
public void removeLanguage() {
dc.remove(PROPERTY_LANGUAGE);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_SPATIAL} property. */
@Nonnull public Opt<String> getSpatial() {
return getFirst(PROPERTY_SPATIAL);
}
/** Set the {@link DublinCore#PROPERTY_SPATIAL} property. */
public void setSpatial(String spatial) {
set(PROPERTY_SPATIAL, spatial);
}
/** Update the {@link DublinCore#PROPERTY_SPATIAL} property. */
public void updateSpatial(Opt<String> spatial) {
update(PROPERTY_SPATIAL, spatial);
}
/** Remove the {@link DublinCore#PROPERTY_SPATIAL} property. */
public void removeSpatial() {
dc.remove(PROPERTY_SPATIAL);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_SOURCE} property. */
@Nonnull public Opt<String> getSource() {
return getFirst(PROPERTY_SOURCE);
}
/** Set the {@link DublinCore#PROPERTY_SOURCE} property. */
public void setSource(String source) {
set(PROPERTY_SOURCE, source);
}
/** Remove the {@link DublinCore#PROPERTY_SOURCE} property. */
public void removeSource() {
dc.remove(PROPERTY_SOURCE);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get all {@link DublinCore#PROPERTY_CONTRIBUTOR} properties. */
@Nonnull public List<String> getContributors() {
return get(PROPERTY_CONTRIBUTOR);
}
/** Set multiple {@link DublinCore#PROPERTY_CONTRIBUTOR} properties. */
public void setContributors(List<String> contributors) {
set(PROPERTY_CONTRIBUTOR, contributors);
}
/** Set the {@link DublinCore#PROPERTY_CONTRIBUTOR} property. */
public void setContributor(String contributor) {
set(PROPERTY_CONTRIBUTOR, contributor);
}
/** Add a {@link DublinCore#PROPERTY_CONTRIBUTOR} property. */
public void addContributor(String contributor) {
add(PROPERTY_CONTRIBUTOR, contributor);
}
/** Update the {@link DublinCore#PROPERTY_CONTRIBUTOR} property. */
public void updateContributor(Opt<String> contributor) {
update(PROPERTY_CONTRIBUTOR, contributor);
}
/** Remove all {@link DublinCore#PROPERTY_CONTRIBUTOR} properties. */
public void removeContributors() {
dc.remove(PROPERTY_CONTRIBUTOR);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_TEMPORAL} property. */
@Nonnull public Opt<Temporal> getTemporal() {
return getFirstVal(PROPERTY_TEMPORAL).map(OpencastMetadataCodec.decodeTemporal);
}
/**
* Set the {@link DublinCore#PROPERTY_TEMPORAL} property.
* The dates are encoded with a precision of {@link Precision#Second}.
*/
public void setTemporal(Date from, Date to) {
setPeriod(PROPERTY_TEMPORAL, from, to, Precision.Second);
}
/** Remove the {@link DublinCore#PROPERTY_TEMPORAL} property. */
public void removeTemporal() {
dc.remove(PROPERTY_TEMPORAL);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/** Get the {@link DublinCore#PROPERTY_TYPE} property split into its components. Components are separated by "/". */
@Nonnull public Opt<Stream<String>> getType() {
return getFirst(PROPERTY_TYPE).map(Strings.split("/"));
}
/** Get the {@link DublinCore#PROPERTY_TYPE} property as a single string. */
@Nonnull public Opt<String> getTypeCombined() {
return getFirst(PROPERTY_TYPE);
}
/**
* Set the {@link DublinCore#PROPERTY_TYPE} property from a type and a subtype.
* Type and subtype are separated by "/".
*/
public void setType(String type, String subtype) {
set(PROPERTY_TYPE, type + "/" + subtype);
}
/** Set the {@link DublinCore#PROPERTY_TYPE} property from a single string. */
public void setType(String type) {
set(PROPERTY_TYPE, type);
}
/** Remove the {@link DublinCore#PROPERTY_TYPE} property. */
public void removeType() {
dc.remove(PROPERTY_TYPE);
}
/* ------------------------------------------------------------------------------------------------------------------ */
public static final class Episode extends OpencastDctermsDublinCore {
public Episode(DublinCoreCatalog dc) {
super(dc);
}
/** Get the {@link DublinCore#PROPERTY_IS_PART_OF} property. */
@Nonnull public Opt<String> getIsPartOf() {
return getFirst(PROPERTY_IS_PART_OF);
}
/** Set the {@link DublinCore#PROPERTY_IS_PART_OF} property. */
public void setIsPartOf(String seriesID) {
set(PROPERTY_IS_PART_OF, seriesID);
}
/** Update the {@link DublinCore#PROPERTY_IS_PART_OF} property. */
public void updateIsPartOf(Opt<String> seriesID) {
update(PROPERTY_IS_PART_OF, seriesID);
}
/** Remove the {@link DublinCore#PROPERTY_IS_PART_OF} property. */
public void removeIsPartOf() {
dc.remove(PROPERTY_IS_PART_OF);
}
}
/* ------------------------------------------------------------------------------------------------------------------ */
public static final class Series extends OpencastDctermsDublinCore {
public Series(DublinCoreCatalog dc) {
super(dc);
}
}
/* ------------------------------------------------------------------------------------------------------------------ */
protected void setDate(EName property, Date date, Precision p) {
dc.set(property, OpencastMetadataCodec.encodeDate(date, p));
}
protected void updateDate(EName property, Opt<Date> date, Precision p) {
for (Date d : date) {
setDate(property, d, p);
}
}
/** Encode with {@link Precision#Second}. */
protected void setPeriod(EName property, Date from, Date to, Precision p) {
dc.set(property, OpencastMetadataCodec.encodePeriod(from, to, p));
}
protected List<String> get(EName property) {
return dc.get(property, LANGUAGE_ANY);
}
/** Like {@link DublinCore#getFirst(EName)} but with the result wrapped in an Opt. */
protected Opt<String> getFirst(EName property) {
return Opt.nul(dc.getFirst(property));
}
/** Like {@link DublinCore#getFirstVal(EName)} but with the result wrapped in an Opt. */
protected Opt<DublinCoreValue> getFirstVal(EName property) {
return Opt.nul(dc.getFirstVal(property));
}
protected void set(EName property, String value) {
if (StringUtils.isNotBlank(value)) {
dc.set(property, value);
}
}
protected void set(EName property, List<String> values) {
final List<DublinCoreValue> valuesFiltered = $(values).filter(Strings.isNotBlank).map(mkValue).toList();
if (!valuesFiltered.isEmpty()) {
dc.remove(property);
dc.set(property, valuesFiltered);
}
}
protected void add(EName property, String value) {
if (StringUtils.isNotBlank(value)) {
dc.add(property, value);
}
}
protected void update(EName property, Opt<String> value) {
for (String v : value) {
set(property, v);
}
}
private final Fn<String, DublinCoreValue> mkValue = new Fn<String, DublinCoreValue>() {
@Override public DublinCoreValue apply(String v) {
return DublinCoreValue.mk(v);
}
};
}