/** * 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.mediapackage; import static java.lang.String.format; import org.opencastproject.util.data.Function; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * ELement flavors describe {@link MediaPackageElement}s in a semantic way. They reveal or give at least a hint about * the meaning of an element. * */ @XmlJavaTypeAdapter(MediaPackageElementFlavor.FlavorAdapter.class) public class MediaPackageElementFlavor implements Cloneable, Comparable<MediaPackageElementFlavor>, Serializable { public static final String WILDCARD = "*"; /** * Serial version uid */ private static final long serialVersionUID = 1L; /** * Character that separates both parts of a flavor */ private static final String SEPARATOR = "/"; /** * String representation of type */ private String type = null; /** * String representation of subtype */ private String subtype = null; /** * Alternate representations for type/subtype */ private List<ElementTypeEquivalent> equivalents = new ArrayList<ElementTypeEquivalent>(); /** * Main description */ private String description = null; /** * Creates a new element type with the given type, subtype and a description. * * @param type * the major type * @param subtype * minor type * @param description * an optional description */ public MediaPackageElementFlavor(String type, String subtype, String description) { this.type = checkPartSyntax(type); this.subtype = checkPartSyntax(subtype); this.description = description; } /** * Checks that any of the parts this flavor consists of abide to the syntax restrictions * * @param part * @return */ private String checkPartSyntax(String part) { // Parts may not be null if (part == null) throw new IllegalArgumentException("Flavor parts may not be null!"); // Parts may not contain the flavor separator character if (part.contains(SEPARATOR)) throw new IllegalArgumentException( format("Invalid flavor part \"%s\". Flavor parts may not contain '%s'!", part, SEPARATOR)); // Parts may not contain leading and trailing blanks, and may only consist of lowercase letters String adaptedPart = part.trim().toLowerCase(); // Parts may not be empty if (adaptedPart.isEmpty()) throw new IllegalArgumentException( format("Invalid flavor part \"%s\". Flavor parts may not be blank or empty!", part)); return adaptedPart; } public MediaPackageElementFlavor() { } /** * Creates a new element type with the given type and subtype. * * @param type * the major type * @param subtype * minor type */ public MediaPackageElementFlavor(String type, String subtype) { this(type, subtype, null); } /** Constructor function for {@link #MediaPackageElementFlavor(String, String)}. */ public static MediaPackageElementFlavor flavor(String type, String subtype) { return new MediaPackageElementFlavor(type, subtype); } /** * Returns the major type of this element type. Major types are more of a technical description. * <p> * For example, if the element type is a presentation movie which is represented as <code>presentation/source</code>, * this method will return <code>track</code>. * * @return the type */ public String getType() { return type; } /** * Returns the minor type of this element type. Minor types define the meaning. * <p> * For example, if the element type is a presentation movie which is represented as <code>presentation/source</code>, * this method will return <code>presentation</code>. * * @return the subtype */ public String getSubtype() { return subtype; } /** * Returns the element type description. * * @return the description */ public String getDescription() { return description; } /** * Sets the element type description. */ public void setDescription(String description) { this.description = description; } /** * Adds an equivalent type / subtype definition for this element type. * * @param type * major type * @param subtype * minor type * @throws IllegalArgumentException * if any of the arguments is <code>null</code> */ public void addEquivalent(String type, String subtype) throws IllegalArgumentException { if (type == null) throw new IllegalArgumentException("Type must not be null!"); if (subtype == null) throw new IllegalArgumentException("Subtype must not be null!"); equivalents.add(new ElementTypeEquivalent(type, subtype)); } /** * @see java.lang.Object#clone() */ @Override public MediaPackageElementFlavor clone() throws CloneNotSupportedException { MediaPackageElementFlavor m = (MediaPackageElementFlavor) super.clone(); m.type = this.type; m.subtype = this.subtype; m.description = this.description; m.equivalents.addAll(equivalents); return m; } /** * Returns <code>true</code> if this element type is an equivalent for the specified type and subtype. * <p> * For example, a gzipped file may have both of these element types defined, <code>application/x-compressed</code> or * <code>application/x-gzip</code>. * * @return <code>true</code> if this mime type is an equivalent */ public boolean isEquivalentTo(String type, String subtype) { if (this.type.equalsIgnoreCase(type) && this.subtype.equalsIgnoreCase(subtype)) return true; if (equivalents != null) { for (ElementTypeEquivalent equivalent : equivalents) { if (equivalent.matches(type, subtype)) return true; } } return false; } /** * Defines equality between flavors and strings. * * @param flavor * string of the form "type/subtype" */ public boolean eq(String flavor) { return flavor != null && flavor.equals(toString()); } /** * @see java.lang.String#compareTo(java.lang.Object) */ @Override public int compareTo(MediaPackageElementFlavor m) { return toString().compareTo(m.toString()); } /** * Returns the flavor as a string "type/subtype". */ @Override public String toString() { return type + SEPARATOR + subtype; } /** * Creates a new media package element flavor. * * @param s * the media package flavor * @return the media package element flavor object * @throws IllegalArgumentException * if the string <code>s</code> does not contain a <i>dash</i> to divide the type from subtype. */ public static MediaPackageElementFlavor parseFlavor(String s) throws IllegalArgumentException { if (s == null) throw new IllegalArgumentException("Unable to create element flavor from 'null'"); String[] parts = s.split(SEPARATOR); if (parts.length != 2) throw new IllegalArgumentException(format("Unable to create element flavor from \"%s\"", s)); return new MediaPackageElementFlavor(parts[0], parts[1]); } public static final Function<String, MediaPackageElementFlavor> parseFlavor = new Function<String, MediaPackageElementFlavor>() { @Override public MediaPackageElementFlavor apply(String s) { return parseFlavor(s); } }; /** Check if <code>type</code> is a {@link #WILDCARD wildcard}. */ public static boolean isWildcard(String type) { return WILDCARD.equals(type); } /** Check if type or subtype of <code>flavor</code> is a wildcard. */ public static boolean hasWildcard(MediaPackageElementFlavor flavor) { return isWildcard(flavor.getType()) || isWildcard(flavor.getSubtype()); } /** * Helper class to store type/subtype equivalents for a given element type. */ private class ElementTypeEquivalent implements Serializable { /** * Serial version uid */ private static final long serialVersionUID = 1L; private String innerType; private String innerSubtype; ElementTypeEquivalent(String type, String subtype) { innerType = type.trim().toLowerCase(); innerSubtype = subtype.trim().toLowerCase(); } boolean matches(String type, String subtype) { return innerType.equalsIgnoreCase(type) && innerSubtype.equalsIgnoreCase(subtype); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + ((innerSubtype == null) ? 0 : innerSubtype.hashCode()); result = prime * result + ((innerType == null) ? 0 : innerType.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof ElementTypeEquivalent)) return false; ElementTypeEquivalent other = (ElementTypeEquivalent) obj; if (!getOuterType().equals(other.getOuterType())) return false; if (innerSubtype == null) { if (other.innerSubtype != null) return false; } else if (!innerSubtype.equals(other.innerSubtype)) return false; if (innerType == null) { if (other.innerType != null) return false; } else if (!innerType.equals(other.innerType)) return false; return true; } private MediaPackageElementFlavor getOuterType() { return MediaPackageElementFlavor.this; } } /** * JAXB adapter implementation. */ static class FlavorAdapter extends XmlAdapter<String, MediaPackageElementFlavor> { @Override public String marshal(MediaPackageElementFlavor flavor) throws Exception { if (flavor == null) { return null; } else { return flavor.toString(); } } @Override public MediaPackageElementFlavor unmarshal(String str) throws Exception { MediaPackageElementFlavor f = parseFlavor(str); return f; } } public boolean matches(MediaPackageElementFlavor other) { if (other == null) return false; if (this == other) return true; if (subtype == null) { if (other.subtype != null && !isWildcard(other.subtype)) return false; } else if (!subtype.equals(other.subtype) && (!isWildcard(subtype) && !isWildcard(other.subtype))) return false; if (type == null) { if (other.type != null && !isWildcard(other.type)) return false; } else if (!type.equals(other.type) && (!isWildcard(type) && !isWildcard(other.type))) return false; return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((subtype == null) ? 0 : subtype.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof MediaPackageElementFlavor)) return false; MediaPackageElementFlavor other = (MediaPackageElementFlavor) obj; if (subtype == null) { if (other.subtype != null) return false; } else if (!subtype.equals(other.subtype)) return false; if (type == null) { if (other.type != null) return false; } else if (!type.equals(other.type)) return false; return true; } }