View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  package org.fourthline.cling.support.model.dlna;
16  
17  import org.seamless.util.Exceptions;
18  
19  import java.util.HashMap;
20  import java.util.Locale;
21  import java.util.Map;
22  import java.util.logging.Level;
23  import java.util.logging.Logger;
24  
25  /**
26   * Transforms known and standardized DLNA attributes from/to string representation.
27   * <p>
28   * The {@link #newInstance(org.fourthline.cling.support.model.dlna.DLNAAttribute.Type, String, String)}
29   * method attempts to instantiate the best header subtype for a given header (name) and string value.
30   * </p>
31   *
32   * @author Christian Bauer
33   * @author Mario Franco
34   */
35  public abstract class DLNAAttribute<T> {
36  
37      final private static Logger log = Logger.getLogger(DLNAAttribute.class.getName());
38  
39      /**
40       * Maps a standardized DLNA attribute to potential attribute subtypes.
41       */
42      public static enum Type {
43  
44          /**
45           * Order is important for DLNAProtocolInfo
46           */ 
47          DLNA_ORG_PN("DLNA.ORG_PN", DLNAProfileAttribute.class),
48          DLNA_ORG_OP("DLNA.ORG_OP", DLNAOperationsAttribute.class),
49          DLNA_ORG_PS("DLNA.ORG_PS", DLNAPlaySpeedAttribute.class),
50          DLNA_ORG_CI("DLNA.ORG_CI", DLNAConversionIndicatorAttribute.class),
51          DLNA_ORG_FLAGS("DLNA.ORG_FLAGS", DLNAFlagsAttribute.class);
52      
53          private static Map<String, Type> byName = new HashMap<String, Type>() {
54              {
55                  for (Type t : Type.values()) {
56                      put(t.getAttributeName().toUpperCase(Locale.ROOT), t);
57                  }
58              }
59          };
60  
61          private String attributeName;
62          private Class<? extends DLNAAttribute>[] attributeTypes;
63  
64          @SafeVarargs
65          private Type(String attributeName, Class<? extends DLNAAttribute>... attributeClass) {
66              this.attributeName = attributeName;
67              this.attributeTypes = attributeClass;
68          }
69  
70          public String getAttributeName() {
71              return attributeName;
72          }
73  
74          public Class<? extends DLNAAttribute>[] getAttributeTypes() {
75              return attributeTypes;
76          }
77  
78          public static Type valueOfAttributeName(String attributeName) {
79              if (attributeName == null) {
80                  return null;
81              }
82              return byName.get(attributeName.toUpperCase(Locale.ROOT));
83          }
84      }
85  
86      private T value;
87  
88      public void setValue(T value) {
89          this.value = value;
90      }
91  
92      public T getValue() {
93          return value;
94      }
95  
96      /**
97       * @param s  This attribute's value as a string representation.
98       * @param cf This attribute's mime type as a string representation, optional.
99       * @throws InvalidDLNAProtocolAttributeException
100      *          If the value is invalid for this DLNA attribute.
101      */
102     public abstract void setString(String s, String cf) throws InvalidDLNAProtocolAttributeException;
103 
104     /**
105      * @return A string representing this attribute's value.
106      */
107     public abstract String getString();
108 
109     /**
110      * Create a new instance of a {@link DLNAAttribute} subtype that matches the given type and value.
111      * <p>
112      * This method iterates through all potential attribute subtype classes as declared in {@link Type}.
113      * It creates a new instance of the subtype class and calls its {@link #setString(String, String)} method.
114      * If no {@link org.fourthline.cling.support.model.dlna.InvalidDLNAProtocolAttributeException} is thrown,
115      * the subtype instance is returned.
116      * </p>
117      *
118      * @param type           The type of the attribute.
119      * @param attributeValue The value of the attribute.
120      * @param contentFormat  The DLNA mime type of the attribute, optional.
121      * @return The best matching attribute subtype instance, or <code>null</code> if no subtype can be found.
122      */
123     public static DLNAAttribute newInstance(DLNAAttribute.Type type, String attributeValue, String contentFormat) {
124 
125         DLNAAttribute attr = null;
126         for (int i = 0; i < type.getAttributeTypes().length && attr == null; i++) {
127             Class<? extends DLNAAttribute> attributeClass = type.getAttributeTypes()[i];
128             try {
129                 log.finest("Trying to parse DLNA '" + type + "' with class: " + attributeClass.getSimpleName());
130                 attr = attributeClass.newInstance();
131                 if (attributeValue != null) {
132                     attr.setString(attributeValue, contentFormat);
133                 }
134             } catch (InvalidDLNAProtocolAttributeException ex) {
135                 log.finest("Invalid DLNA attribute value for tested type: " + attributeClass.getSimpleName() + " - " + ex.getMessage());
136                 attr = null;
137             } catch (Exception ex) {
138                 log.severe("Error instantiating DLNA attribute of type '" + type + "' with value: " + attributeValue);
139                 log.log(Level.SEVERE, "Exception root cause: ", Exceptions.unwrap(ex));
140             }
141         }
142         return attr;
143     }
144 
145     @Override
146     public String toString() {
147         return "(" + getClass().getSimpleName() + ") '" + getValue() + "'";
148     }
149 }