View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.mediapackage;
23  
24  import static java.lang.String.format;
25  
26  import java.io.Serializable;
27  import java.util.Objects;
28  
29  import javax.xml.bind.annotation.adapters.XmlAdapter;
30  import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
31  
32  /**
33   * ELement flavors describe {@link MediaPackageElement}s in a semantic way. They reveal or give at least a hint about
34   * the meaning of an element.
35   *
36   */
37  @XmlJavaTypeAdapter(MediaPackageElementFlavor.FlavorAdapter.class)
38  public class MediaPackageElementFlavor implements Cloneable, Comparable<MediaPackageElementFlavor>, Serializable {
39  
40    /**
41     * Wildcard character used in type and subtype
42     */
43    public static final String WILDCARD = "*";
44  
45    /**
46     * Serial version uid
47     */
48    private static final long serialVersionUID = 1L;
49  
50    /**
51     * Character that separates both parts of a flavor
52     */
53    public static final String SEPARATOR = "/";
54  
55    /**
56     * String representation of type
57     */
58    private String type = null;
59  
60    /**
61     * String representation of subtype
62     */
63    private String subtype = null;
64  
65  
66    private MediaPackageElementFlavor() {
67    }
68  
69    /**
70     * Creates a new flavor with the given type and subtype.
71     *
72     * @param type
73     *          the type of the flavor
74     * @param subtype
75     *          the subtype of the flavor
76     */
77    public MediaPackageElementFlavor(String type, String subtype) {
78      this.type = checkPartSyntax(type);
79      this.subtype = checkPartSyntax(subtype);
80    }
81  
82    /**
83     * Checks that any of the parts this flavor consists of abide to the syntax restrictions
84     *
85     * @param part
86     * @return
87     */
88    private String checkPartSyntax(String part) {
89      // Parts may not be null
90      if (part == null)
91        throw new IllegalArgumentException("Flavor parts may not be null!");
92  
93      // Parts may not contain the flavor separator character
94      if (part.contains(SEPARATOR))
95        throw new IllegalArgumentException(
96                format("Invalid flavor part \"%s\". Flavor parts may not contain '%s'!", part, SEPARATOR));
97  
98      // Parts may not contain leading and trailing blanks, and may only consist of lowercase letters
99      String adaptedPart = part.trim().toLowerCase();
100 
101     // Parts may not be empty
102     if (adaptedPart.isEmpty())
103       throw new IllegalArgumentException(
104               format("Invalid flavor part \"%s\". Flavor parts may not be blank or empty!", part));
105 
106     return adaptedPart;
107   }
108 
109   /** Constructor function for {@link #MediaPackageElementFlavor(String, String)}. */
110   public static MediaPackageElementFlavor flavor(String type, String subtype) {
111     return new MediaPackageElementFlavor(type, subtype);
112   }
113 
114   /**
115    * Returns the type of this flavor.
116    * The type is never <code>null</code>.
117    * <p>
118    * For example, if the type is a presentation movie which is represented as <code>presentation/source</code>,
119    * this method will return <code>presentation</code>.
120    *
121    * @return the type of this flavor
122    */
123   public String getType() {
124     return type;
125   }
126 
127   /**
128    * Returns the subtype of this flavor.
129    * The subtype is never <code>null</code>.
130    * <p>
131    * For example, if the subtype is a presentation movie which is represented as <code>presentation/source</code>,
132    * this method will return <code>source</code>.
133    *
134    * @return the subtype
135    */
136   public String getSubtype() {
137     return subtype;
138   }
139 
140   /**
141    * "Applies" this flavor to the given target flavor. E.g. applying '*\/preview' to 'presenter/source' yields
142    * 'presenter/preview', applying 'presenter/*' to 'foo/source' yields 'presenter/source', and applying 'foo/bar' to
143    * 'presenter/source' yields 'foo/bar'.
144    *
145    * @param target The target flavor to apply this flavor to.
146    *
147    * @return The resulting flavor.
148    */
149   public MediaPackageElementFlavor applyTo(MediaPackageElementFlavor target) {
150     String type = this.type;
151     String subtype = this.subtype;
152     if (WILDCARD.equals(this.type)) {
153       type = target.getType();
154     }
155     if (WILDCARD.equals(this.subtype)) {
156       subtype = target.getSubtype();
157     }
158     return new MediaPackageElementFlavor(type, subtype);
159   }
160 
161   /**
162    * @see java.lang.Object#clone()
163    */
164   @Override
165   public MediaPackageElementFlavor clone() throws CloneNotSupportedException {
166     MediaPackageElementFlavor m = (MediaPackageElementFlavor) super.clone();
167     m.type = this.type;
168     m.subtype = this.subtype;
169     return m;
170   }
171 
172   /**
173    * Defines equality between flavors and strings.
174    *
175    * @param flavor
176    *          string of the form "type/subtype"
177    */
178   public boolean eq(String flavor) {
179     return flavor != null && flavor.equals(toString());
180   }
181 
182   /**
183    * @see java.lang.String#compareTo(java.lang.Object)
184    */
185   @Override
186   public int compareTo(MediaPackageElementFlavor m) {
187     return toString().compareTo(m.toString());
188   }
189 
190   /**
191    * Returns the flavor as a string "type/subtype".
192    */
193   @Override
194   public String toString() {
195     return type + SEPARATOR + subtype;
196   }
197 
198   /**
199    * Creates a new media package element flavor.
200    *
201    * @param s
202    *          the media package flavor
203    * @return the media package element flavor object
204    * @throws IllegalArgumentException
205    *           if the string <code>s</code> does not contain a <i>dash</i> to divide the type from subtype.
206    */
207   public static MediaPackageElementFlavor parseFlavor(String s) throws IllegalArgumentException {
208     if (s == null)
209       throw new IllegalArgumentException("Unable to create element flavor from 'null'");
210     String[] parts = s.split(SEPARATOR);
211     if (parts.length != 2)
212       throw new IllegalArgumentException(format("Unable to create element flavor from \"%s\"", s));
213     return new MediaPackageElementFlavor(parts[0], parts[1]);
214   }
215 
216   /**
217    * JAXB adapter implementation.
218    */
219   static class FlavorAdapter extends XmlAdapter<String, MediaPackageElementFlavor> {
220     @Override
221     public String marshal(MediaPackageElementFlavor flavor) throws Exception {
222       if (flavor == null) {
223         return null;
224       }
225       return flavor.toString();
226     }
227 
228     @Override
229     public MediaPackageElementFlavor unmarshal(String str) throws Exception {
230       return parseFlavor(str);
231     }
232   }
233 
234   /**
235    * Check if types match.
236    * Two types match if they are equal or if one of them is a {@link #WILDCARD wildcard}.
237    **/
238   private static boolean typeMatches(String a, String b) {
239     return a.equals(b) || WILDCARD.equals(a) || WILDCARD.equals(b);
240   }
241 
242   /**
243    * Check if two flavors match.
244    * Two flavors match if both their type and subtype matches.
245    *
246    * @param other
247    *          Flavor to compare against
248    * @return  If the flavors match
249    */
250   public boolean matches(MediaPackageElementFlavor other) {
251     return other != null
252       && typeMatches(type, other.type)
253       && typeMatches(subtype, other.subtype);
254   }
255 
256   @Override
257   public int hashCode() {
258     return Objects.hash(type, subtype);
259   }
260 
261   @Override
262   public boolean equals(Object other) {
263     return (other instanceof MediaPackageElementFlavor)
264       && type.equals(((MediaPackageElementFlavor) other).type)
265       && subtype.equals(((MediaPackageElementFlavor) other).subtype);
266   }
267 
268 }