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;
17  
18  import java.net.InetAddress;
19  import java.net.NetworkInterface;
20  import java.util.Collections;
21  import java.util.Enumeration;
22  import java.util.Set;
23  import java.util.Locale;
24  
25  /**
26   * Shared trivial procedures.
27   *
28   * @author Christian Bauer
29   */
30  public class ModelUtil {
31  
32      /**
33       * True if this class is executing on an Android runtime
34       */
35      final public static boolean ANDROID_RUNTIME;
36      static {
37          boolean foundAndroid = false;
38          try {
39              Class androidBuild = Thread.currentThread().getContextClassLoader().loadClass("android.os.Build");
40              foundAndroid = androidBuild.getField("ID").get(null) != null;
41          } catch (Exception ex) {
42              // Ignore
43          }
44          ANDROID_RUNTIME = foundAndroid;
45      }
46  
47      /**
48       * True if this class is executing on an Android emulator runtime.
49       */
50      final public static boolean ANDROID_EMULATOR;
51      static {
52          boolean foundEmulator = false;
53          try {
54              Class androidBuild = Thread.currentThread().getContextClassLoader().loadClass("android.os.Build");
55              String product = (String)androidBuild.getField("PRODUCT").get(null);
56              if ("google_sdk".equals(product) || ("sdk".equals(product)))
57                  foundEmulator = true;
58          } catch (Exception ex) {
59              // Ignore
60          }
61          ANDROID_EMULATOR = foundEmulator;
62      }
63  
64      /**
65       * @param stringConvertibleTypes A collection of interfaces.
66       * @param clazz An interface to test.
67       * @return <code>true</code> if the given interface is an Enum, or if the collection contains a super-interface.
68       */
69      public static boolean isStringConvertibleType(Set<Class> stringConvertibleTypes, Class clazz) {
70          if (clazz.isEnum()) return true;
71          for (Class toStringOutputType : stringConvertibleTypes) {
72              if (toStringOutputType.isAssignableFrom(clazz)) {
73                  return true;
74              }
75          }
76          return false;
77      }
78  
79      /**
80       * @param name A UPnP device architecture "name" string.
81       * @return <code>true</code> if the name is not empty, doesn't start with "xml", and
82       *         matches {@link org.fourthline.cling.model.Constants#REGEX_UDA_NAME}.
83       */
84      public static boolean isValidUDAName(String name) {
85          if (ANDROID_RUNTIME) {
86              return name != null && name.length() != 0;
87          }
88          return name != null && name.length() != 0 && !name.toLowerCase(Locale.ROOT).startsWith("xml") && name.matches(Constants.REGEX_UDA_NAME);
89      }
90  
91      /**
92       * Wraps the checked exception in a runtime exception.
93       */
94      public static InetAddress getInetAddressByName(String name) {
95          try {
96              return InetAddress.getByName(name);
97          } catch (Exception ex) {
98              throw new RuntimeException(ex);
99          }
100     }
101 
102     /**
103      * Converts the given instances into comma-separated elements of a string,
104      * escaping commas with backslashes.
105      */
106     public static String toCommaSeparatedList(Object[] o) {
107         return toCommaSeparatedList(o, true, false);
108     }
109 
110     /**
111      * Converts the given instances into comma-separated elements of a string,
112      * optionally escapes commas and double quotes with backslahses.
113      */
114     public static String toCommaSeparatedList(Object[] o, boolean escapeCommas, boolean escapeDoubleQuotes) {
115         if (o == null) {
116             return "";
117         }
118         StringBuilder sb = new StringBuilder();
119         for (Object obj : o) {
120             String objString = obj.toString();
121             objString = objString.replaceAll("\\\\", "\\\\\\\\"); // Replace one backslash with two (nice, eh?)
122             if (escapeCommas) {
123                 objString = objString.replaceAll(",", "\\\\,");
124             }
125             if (escapeDoubleQuotes) {
126                 objString = objString.replaceAll("\"", "\\\"");
127             }
128             sb.append(objString).append(",");
129         }
130         if (sb.length() > 1) {
131             sb.deleteCharAt(sb.length() - 1);
132         }
133         return sb.toString();
134 
135     }
136 
137     /**
138      * Converts the comma-separated elements of a string into an array of strings,
139      * unescaping backslashed commas.
140      */
141     public static String[] fromCommaSeparatedList(String s) {
142         return fromCommaSeparatedList(s, true);
143     }
144 
145     /**
146      * Converts the comma-separated elements of a string into an array of strings,
147      * optionally unescaping backslashed commas.
148      */
149     public static String[] fromCommaSeparatedList(String s, boolean unescapeCommas) {
150         if (s == null || s.length() == 0) {
151             return null;
152         }
153 
154         final String QUOTED_COMMA_PLACEHOLDER = "XXX1122334455XXX";
155         if (unescapeCommas) {
156             s = s.replaceAll("\\\\,", QUOTED_COMMA_PLACEHOLDER);
157         }
158 
159         String[] split = s.split(",");
160         for (int i = 0; i < split.length; i++) {
161             split[i] = split[i].replaceAll(QUOTED_COMMA_PLACEHOLDER, ",");
162             split[i] = split[i].replaceAll("\\\\\\\\", "\\\\");
163         }
164         return split;
165     }
166 
167     /**
168      * @param seconds The number of seconds to convert.
169      * @return A string representing hours, minutes, seconds, e.g. <code>11:23:44</code>
170      */
171     public static String toTimeString(long seconds) {
172         long hours = seconds / 3600,
173                 remainder = seconds % 3600,
174                 minutes = remainder / 60,
175                 secs = remainder % 60;
176 
177         return ((hours < 10 ? "0" : "") + hours
178                 + ":" + (minutes < 10 ? "0" : "") + minutes
179                 + ":" + (secs < 10 ? "0" : "") + secs);
180     }
181 
182     /**
183      * @param s A string representing hours, minutes, seconds, e.g. <code>11:23:44</code>
184      * @return The converted number of seconds.
185      */
186     public static long fromTimeString(String s) {
187         // Handle "00:00:00.000" pattern, drop the milliseconds
188         if (s.lastIndexOf(".") != -1)
189             s = s.substring(0, s.lastIndexOf("."));
190         String[] split = s.split(":");
191         if (split.length != 3)
192             throw new IllegalArgumentException("Can't parse time string: " + s);
193         return (Long.parseLong(split[0]) * 3600) +
194                 (Long.parseLong(split[1]) * 60) +
195                 (Long.parseLong(split[2]));
196     }
197 
198     /**
199      * @param s A string with commas.
200      * @return The same string, a newline appended after every comma.
201      */
202     public static String commaToNewline(String s) {
203         StringBuilder sb = new StringBuilder();
204         String[] split = s.split(",");
205         for (String splitString : split) {
206             sb.append(splitString).append(",").append("\n");
207         }
208         if (sb.length() > 2) {
209             sb.deleteCharAt(sb.length() - 2);
210         }
211         return sb.toString();
212     }
213 
214     /**
215      * DNS reverse name lookup.
216      *
217      * @param includeDomain <code>true</code> if the whole FQDN should be returned, instead of just the first (host) part.
218      * @return The resolved host (and domain-) name, or "UNKNOWN HOST" if resolution failed.
219      */
220     public static String getLocalHostName(boolean includeDomain) {
221         try {
222             String hostname = InetAddress.getLocalHost().getHostName();
223             return includeDomain
224                     ? hostname
225                     : hostname.indexOf(".") != -1 ? hostname.substring(0, hostname.indexOf(".")) : hostname;
226 
227         } catch (Exception ex) {
228             // Return a dummy String
229             return "UNKNOWN HOST";
230         }
231     }
232 
233     /**
234      * @return The MAC hardware address of the first network interface of this host.
235      */
236     public static byte[] getFirstNetworkInterfaceHardwareAddress() {
237         try {
238             Enumeration<NetworkInterface> interfaceEnumeration = NetworkInterface.getNetworkInterfaces();
239             for (NetworkInterface iface : Collections.list(interfaceEnumeration)) {
240                 if (!iface.isLoopback() && iface.isUp() && iface.getHardwareAddress() != null) {
241                     return iface.getHardwareAddress();
242                 }
243             }
244         } catch (Exception ex) {
245             throw new RuntimeException("Could not discover first network interface hardware address");
246         }
247         throw new RuntimeException("Could not discover first network interface hardware address");
248     }
249 
250 }