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  
16  package org.fourthline.cling.model.message.header;
17  
18  import org.seamless.util.Exceptions;
19  
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.Locale;
23  import java.util.logging.Level;
24  import java.util.logging.Logger;
25  
26  /**
27   * Transforms known and standardized UPnP/HTTP headers from/to string representation.
28   * <p>
29   * The {@link #newInstance(org.fourthline.cling.model.message.header.UpnpHeader.Type, String)} method
30   * attempts to instantiate the best header subtype for a given header (name) and string value.
31   * </p>
32   *
33   * @author Christian Bauer
34   */
35  public abstract class UpnpHeader<T> {
36  
37      final private static Logger log = Logger.getLogger(UpnpHeader.class.getName());
38  
39      /**
40       * Maps a standardized UPnP header to potential header subtypes.
41       */
42      public static enum Type {
43  
44          USN("USN",
45                  USNRootDeviceHeader.class,
46                  DeviceUSNHeader.class,
47                  ServiceUSNHeader.class,
48                  UDNHeader.class
49          ),
50          NT("NT",
51                  RootDeviceHeader.class,
52                  UDADeviceTypeHeader.class,
53                  UDAServiceTypeHeader.class,
54                  DeviceTypeHeader.class,
55                  ServiceTypeHeader.class,
56                  UDNHeader.class,
57                  NTEventHeader.class
58          ),
59          NTS("NTS", NTSHeader.class),
60          HOST("HOST", HostHeader.class),
61          SERVER("SERVER", ServerHeader.class),
62          LOCATION("LOCATION", LocationHeader.class),
63          MAX_AGE("CACHE-CONTROL", MaxAgeHeader.class),
64          USER_AGENT("USER-AGENT", UserAgentHeader.class),
65          CONTENT_TYPE("CONTENT-TYPE", ContentTypeHeader.class),
66          MAN("MAN", MANHeader.class),
67          MX("MX", MXHeader.class),
68          ST("ST",
69                  STAllHeader.class,
70                  RootDeviceHeader.class,
71                  UDADeviceTypeHeader.class,
72                  UDAServiceTypeHeader.class,
73                  DeviceTypeHeader.class,
74                  ServiceTypeHeader.class,
75                  UDNHeader.class
76          ),
77          EXT("EXT", EXTHeader.class),
78          SOAPACTION("SOAPACTION", SoapActionHeader.class),
79          TIMEOUT("TIMEOUT", TimeoutHeader.class),
80          CALLBACK("CALLBACK", CallbackHeader.class),
81          SID("SID", SubscriptionIdHeader.class),
82          SEQ("SEQ", EventSequenceHeader.class),
83          RANGE("RANGE", RangeHeader.class),
84          CONTENT_RANGE("CONTENT-RANGE", ContentRangeHeader.class),
85          PRAGMA("PRAGMA", PragmaHeader.class),
86          
87          EXT_IFACE_MAC("X-CLING-IFACE-MAC", InterfaceMacHeader.class),
88          EXT_AV_CLIENT_INFO("X-AV-CLIENT-INFO", AVClientInfoHeader.class);
89  
90          private static Map<String, Type> byName = new HashMap<String, Type>() {{
91              for (Type t : Type.values()) {
92                  put(t.getHttpName(), t);
93              }
94          }};
95  
96          private String httpName;
97          private Class<? extends UpnpHeader>[] headerTypes;
98  
99          @SafeVarargs
100         private Type(String httpName, Class<? extends UpnpHeader>... headerClass) {
101             this.httpName = httpName;
102             this.headerTypes = headerClass;
103         }
104 
105         public String getHttpName() {
106             return httpName;
107         }
108 
109         public Class<? extends UpnpHeader>[] getHeaderTypes() {
110             return headerTypes;
111         }
112 
113         public boolean isValidHeaderType(Class<? extends UpnpHeader> clazz) {
114             for (Class<? extends UpnpHeader> permissibleType : getHeaderTypes()) {
115                 if (permissibleType.isAssignableFrom(clazz)) {
116                     return true;
117                 }
118             }
119             return false;
120         }
121 
122         /**
123          * @param httpName A case-insensitive HTTP header name.
124          */
125         public static Type getByHttpName(String httpName) {
126             if (httpName == null) return null;
127         	return byName.get(httpName.toUpperCase(Locale.ROOT));
128         }
129     }
130 
131     private T value;
132 
133     public void setValue(T value) {
134         this.value = value;
135     }
136 
137     public T getValue() {
138         return value;
139     }
140 
141     /**
142      * @param s This header's value as a string representation.
143      * @throws InvalidHeaderException If the value is invalid for this UPnP header.
144      */
145     public abstract void setString(String s) throws InvalidHeaderException;
146 
147     /**
148      * @return A string representing this header's value.
149      */
150     public abstract String getString();
151 
152     /**
153      * Create a new instance of a {@link UpnpHeader} subtype that matches the given type and value.
154      * <p>
155      * This method iterates through all potential header subtype classes as declared in {@link Type}.
156      * It creates a new instance of the subtype class and calls its {@link #setString(String)} method.
157      * If no {@link org.fourthline.cling.model.message.header.InvalidHeaderException} is thrown, the subtype
158      * instance is returned.
159      * </p>
160      *
161      * @param type The type (or name) of the header.
162      * @param headerValue The value of the header.
163      * @return The best matching header subtype instance, or <code>null</code> if no subtype can be found.
164      */
165     public static UpnpHeader newInstance(UpnpHeader.Type type, String headerValue) {
166 
167         // Try all the UPnP headers and see if one matches our value parsers
168         UpnpHeader upnpHeader = null;
169         for (int i = 0; i < type.getHeaderTypes().length && upnpHeader == null; i++) {
170             Class<? extends UpnpHeader> headerClass = type.getHeaderTypes()[i];
171             try {
172                 log.finest("Trying to parse '" + type + "' with class: " + headerClass.getSimpleName());
173                 upnpHeader = headerClass.newInstance();
174                 if (headerValue != null) {
175                     upnpHeader.setString(headerValue);
176                 }
177             } catch (InvalidHeaderException ex) {
178                 log.finest("Invalid header value for tested type: " + headerClass.getSimpleName() + " - " + ex.getMessage());
179                 upnpHeader = null;
180             } catch (Exception ex) {
181                 log.severe("Error instantiating header of type '" + type + "' with value: " + headerValue);
182                 log.log(Level.SEVERE, "Exception root cause: ", Exceptions.unwrap(ex));
183             }
184 
185         }
186         return upnpHeader;
187     }
188 
189     @Override
190     public String toString() {
191         return "(" + getClass().getSimpleName() + ") '" + getValue() + "'";
192     }
193 }