Committed the initial version of the UPnP Extra bundle (FELIX-52).
It requires org.osgi.compendium

git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@391437 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.upnp.extra/pom.xml b/org.apache.felix.upnp.extra/pom.xml
new file mode 100644
index 0000000..31a8734
--- /dev/null
+++ b/org.apache.felix.upnp.extra/pom.xml
@@ -0,0 +1,50 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+  <parent>

+    <groupId>org.apache.felix</groupId>

+    <artifactId>felix</artifactId>

+    <version>0.8.0-SNAPSHOT</version>

+  </parent>

+  <modelVersion>4.0.0</modelVersion>

+  <packaging>osgi-bundle</packaging>

+  <groupId>org.apache.felix</groupId>

+  <name>Apache Felix UPnP Extra </name>

+  <artifactId>org.apache.felix.upnp.extra</artifactId>

+  <version>0.1.0-SNAPSHOT</version>

+  <!-- <url>http://maven.apache.org</url> -->

+  <dependencies>

+    <dependency>

+      <groupId>org.osgi</groupId>

+      <artifactId>org.osgi.compendium</artifactId>

+      <version>4.0</version>

+      <scope>provided</scope>

+    </dependency>

+  </dependencies>

+  <build>

+    <plugins>

+      <plugin>

+        <groupId>org.apache.felix.plugins</groupId>

+        <artifactId>maven-osgi-plugin</artifactId>

+        <!-- <version>${pom.version}</version> -->

+        <extensions>true</extensions>

+        <configuration>

+          <osgiManifest>

+            <bundleName>UPnPExtra</bundleName>

+            <bundleVendor>Apache Software Foundation</bundleVendor>

+            <bundleVersion>0.1.0</bundleVersion>

+            <bundleDescription>

+               	A library used to extend the integration between UPnP and OSGi that is not part of the standard. Also services that allow to change the beahviour of the UPnP Base Driver

+            </bundleDescription>

+            <bundleSymbolicName>org.apache.felix.upnp.extra</bundleSymbolicName>

+            <importPackage>

+			org.osgi.service.upnp;specification-version=1.1,org.apache.felix.upnp.extra.util;specification-version=1.0,org.apache.felix.upnp.extra.controller;specification-version=1.0

+            </importPackage>

+            <exportPackage>

+               	org.apache.felix.upnp.extra.util;specification-version=1.0,org.apache.felix.upnp.extra.controller;specification-version=1.0

+            </exportPackage>

+          </osgiManifest>

+        </configuration>

+      </plugin>

+    </plugins>

+  </build>

+</project>

diff --git a/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/controller/DevicesInfo.java b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/controller/DevicesInfo.java
new file mode 100644
index 0000000..e1ff5f5
--- /dev/null
+++ b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/controller/DevicesInfo.java
@@ -0,0 +1,60 @@
+/*
+ DomoWare UPnP Base Driver is an implementation of the OSGi UnP Device Spcifaction
+ Copyright (C) 2004  Matteo Demuru, Francesco Furfari, Stefano "Kismet" Lenzi
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ You can contact us at:
+ {matte-d, sygent, kismet-sl} [at] users.sourceforge.net
+ */
+package org.apache.felix.upnp.extra.controller;
+
+/**
+ * @author Stefano "Kismet" Lenzi 
+ * @author Francesco Furfari
+ */
+public interface DevicesInfo{
+    /**
+     * 
+     * Allow you to get the URL that poinr to the XML description of
+     * a device specified by UUID. 
+     * 
+     * @param udn the UUID that identify a device
+     * @return The String that rappresent the URL that point to the description of the Device
+     */
+    public String getLocationURL(String udn);
+    
+    /**
+     * 
+     * Allow you to get the URL that poinr to the XML description of
+     * a service specified by ServiceId and UUID of the device that 
+     * contain the service 
+     * 
+     * @param udn the UUID of the device that contain the service
+     * @param serviceId the ServiceId of the service
+     * @return The String that rappresent the URL that point to the description of the Service
+     */
+    public String getSCPDURL(String udn,String serviceId);  
+    
+    /**
+     * Allow you to get the absolue URL of a link that is conatin in a device
+     * 
+     * @param udn the UUID of the UPnP Device 
+     * @param link the relative link that you want to resolve
+     * @return The String that rappresent the absolute URL to the resourse specified by link
+     */
+    public String resolveRelativeUrl(String udn, String link);
+    
+}
diff --git a/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/controller/DriverController.java b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/controller/DriverController.java
new file mode 100644
index 0000000..3b210c6
--- /dev/null
+++ b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/controller/DriverController.java
@@ -0,0 +1,82 @@
+/*
+ DomoWare UPnP Base Driver is an implementation of the OSGi UnP Device Spcifaction
+ Copyright (C) 2004  Matteo Demuru, Francesco Furfari, Stefano "Kismet" Lenzi
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ You can contact us at:
+ {matte-d, sygent, kismet-sl} [at] users.sourceforge.net
+ */
+package org.apache.felix.upnp.extra.controller;
+
+//import java.net.InetAddress;
+
+/**
+ * @author Stefano "Kismet" Lenzi 
+ * @author Francesco Furfari 
+ */
+public interface DriverController {
+	/**
+	 * String for searching all the device on UPnP Network
+	 */
+    public final static String ALL_DEVICE = "ssdp:all";
+    
+    /**
+     * String for searching only root device on UPnP Network
+     */
+    public final static String ROOT_DEVICE = "upnp:rootdevice";
+    /*
+    public InetAddress[] getBindAddress();
+    
+    public void setBindAddress(InetAddress[] IPs);
+    */
+    
+    /**
+     * Set how much messages should be sent by UPnP Base Driver
+     * for debugging purpose
+     * 
+     * @param n the level of log that you want to set
+     */
+    public void setLogLevel(int n);
+    
+    /**
+     * 
+     * @return the actual value of log level
+     */
+    public int getLogLevel();
+    
+    /**
+     * Set if the message of the UPnP Stack should be reported or not
+     * 
+     * @param b true if you want show messages from UPnP Stack false otherwise 
+     */
+    public void setCyberDebug(boolean b);
+    
+    /**
+     * 
+     * @return true if the reporting of UPnP Stack message is active false otherwise
+     */
+    public boolean getCyberDebug();
+    
+    /**
+     * Sent a search message on the UPnP Network, and refresh the device 
+     * founded by UPnP Base Driver 
+     * 
+     * @param target The SSDP string used for the search
+     */
+    public void search(String target);
+
+
+}
diff --git a/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/Constants.java b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/Constants.java
new file mode 100644
index 0000000..c29b3e6
--- /dev/null
+++ b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/Constants.java
@@ -0,0 +1,36 @@
+/*
+ DomoWare UPnP Base Driver is an implementation of the OSGi UnP Device Spcifaction
+ Copyright (C) 2004  Matteo Demuru, Francesco Furfari, Stefano "Kismet" Lenzi
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ You can contact us at:
+ {matte-d, sygent, kismet-sl} [at] users.sourceforge.net
+ */
+package org.apache.felix.upnp.extra.util;
+/**
+ * 
+ * @author Stefano "Kismet" Lenzi 
+ *
+ */
+public interface Constants {
+	/**
+	 * If this property is set on a UPnP Device means that the 
+	 * device service is been created by UPnP base Driver. <br>
+	 * The value of the do not mean nothing. <br>
+	 * The name of the property is "UPnP.device.import".
+	 */
+	public static final String UPNP_IMPORT = "UPnP.device.imported"; 
+}
diff --git a/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/Converter.java b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/Converter.java
new file mode 100644
index 0000000..abd5612
--- /dev/null
+++ b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/Converter.java
@@ -0,0 +1,256 @@
+/*

+DomoWare UPnP Base Driver is an implementation of the OSGi UnP Device Spcifaction

+Copyright (C) 2004  Matteo Demuru, Francesco Furfari, Stefano "Kismet" Lenzi

+

+This library is free software; you can redistribute it and/or

+modify it under the terms of the GNU Lesser General Public

+License as published by the Free Software Foundation; either

+version 2.1 of the License, or (at your option) any later version.

+

+This library is distributed in the hope that it will be useful,

+but WITHOUT ANY WARRANTY; without even the implied warranty of

+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

+Lesser General Public License for more details.

+

+You should have received a copy of the GNU Lesser General Public

+License along with this library; if not, write to the Free Software

+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

+

+You can contact us at:

+	{matte-d, sygent, kismet-sl} [at] users.sourceforge.net

+*/

+

+package org.apache.felix.upnp.extra.util;

+

+import java.text.*;

+import java.util.*;

+

+//import org.apache.xerces.impl.dv.util.*;

+import org.apache.xerces.impl.dv.util.Base64;

+import org.apache.xerces.impl.dv.util.HexBin;

+import org.osgi.service.upnp.*;

+

+/**

+ * @author Stefano "Kismet" Lenzi 

+ * 

+ *

+ */

+public class Converter {

+	

+	/**

+	 * 

+	 * @param value Object that contain the value

+	 * @param upnpType String conating the UPnP Type of the Object

+	 * @return a String that contain the UPnP rappresentation of the value contained in Object

+	 * 		of type specified by typeUPnP

+	 */

+	public static String toString(Object value,String upnpType) throws Exception{

+		if((value==null)||(upnpType==null))

+			throw new NullPointerException("Must be specified a valid value and upnpType");

+		

+		if(value instanceof Number){

+			if(value instanceof Integer){

+				return value.toString();

+			}else if(value instanceof Float){

+				return value.toString();

+			}else if(value instanceof Long){

+				if(upnpType.equals(UPnPStateVariable.TYPE_TIME)){

+					long l = ((Long)value).longValue();

+					if(l<0) throw new IllegalArgumentException(l+ "Must be greater than 0");

+					Calendar c = Calendar.getInstance();

+					c.set(Calendar.HOUR_OF_DAY,(int) (l/3600000));					

+					int x=(int) (l % 3600000);

+					c.set(Calendar.MINUTE,(int) (x / 60000));

+					c.set(Calendar.SECOND,(x % 60000)/1000);

+					SimpleDateFormat sdt = new SimpleDateFormat("HH:mm:ss");

+					return sdt.format(c.getTime());

+				}else if(upnpType.equals(UPnPStateVariable.TYPE_TIME_TZ)){

+					long l = ((Long)value).longValue();

+					if(l<0) throw new IllegalArgumentException(l+ "Must be greater than 0");

+					Calendar c = Calendar.getInstance();

+					c.set(Calendar.HOUR_OF_DAY,(int) (l/3600000));					

+					int x=(int) (l % 3600000);

+					c.set(Calendar.MINUTE,(int) (x / 60000));

+					c.set(Calendar.SECOND,(x % 60000)/1000);

+					SimpleDateFormat sdt = new SimpleDateFormat("HH:mm:ssZ");

+					return sdt.format(c.getTime());

+				}else{

+					//Must be UPnPStateVariable.TYPE_UI4)

+					return value.toString();

+				}

+			}else if(value instanceof Double){

+				if(upnpType.equals(UPnPStateVariable.TYPE_FIXED_14_4)){

+					return Long.toString(((Double)value).longValue())+"."+

+						Integer.toString((int) (((((Double)value).doubleValue()*10000D) % 10000)));  

+				}else{

+					//Must be UPnPStateVariable.TYPE_R8 or UPnPStateVariable.TYPE_NUMBER

+					return value.toString();

+				}

+			}

+		}else if(value instanceof Date){

+			if(upnpType.equals("dateTime")){

+				SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

+				return sdt.format(value);				

+			}else if(upnpType.equals("dateTime.tz")){

+				SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

+				return sdt.format(value);								

+			}else if(upnpType.equals("date")){

+				SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-dd");

+				return sdt.format(value);

+			}

+		}else if(value instanceof Boolean){

+			//Must be UPnPStateVariable.TYPE_BOOLEAN

+			if(((Boolean)value).booleanValue()){

+				return "1";

+			}else{

+				return "0";

+			}

+		}else if(value instanceof Character){

+			//Must be UPnPStateVariable.TYPE_CHAR

+			return value.toString();

+		}else if(value instanceof String){

+			return value.toString();

+			//Must be one of 

+			//	UPnPStateVariable.TYPE_STRING or 

+			//	UPnPStateVariable.TYPE_URI or 

+			//	UPnPStateVariable.TYPE_UUID

+		}else if(value instanceof byte[]){

+			if(upnpType.equals("bin.hex")){

+				return HexBin.encode((byte[]) value);				

+			}else if(upnpType.equals("bin.base64")){

+				return Base64.encode((byte[]) value);

+			}

+		}

+		throw new IllegalArgumentException("Invalid Binding");

+	}

+	

+	/**

+	 * 

+	 * @param value

+	 * @param upnpType

+	 * @return

+	 */

+	public static Object parseString(String value,String upnpType) throws Exception{

+		if (value ==null && upnpType.equals("string"))

+                value = "";

+        if((value==null)||(upnpType==null))

+				throw new NullPointerException("Must be specified a valid value and upnpType");

+		

+		if (upnpType.equals("ui1") || upnpType.equals("ui2")

+				|| upnpType.equals("i1") || upnpType.equals("i2")

+				|| upnpType.equals("i4") || upnpType.equals("int")) {

+			

+			return new Integer(value);

+		} else if (upnpType.equals("ui4")){			

+			return new Long(value);

+		} else if(upnpType.equals("time")){

+			String[] timeFormats=new String[]{"HH:mm:ss"};

+			Date d=getDateValue(value,timeFormats,timeFormats);

+			

+			Calendar c = Calendar.getInstance();

+			c.setTime(d);

+			return new Long(

+					c.get(Calendar.HOUR_OF_DAY)*3600000

+					+c.get(Calendar.MINUTE)*60000

+					+c.get(Calendar.SECOND)*1000

+			);

+		} else if(upnpType.equals("time.tz")) {

+			String[] timeFormats=new String[]{"HH:mm:ssZ","HH:mm:ss"};

+			Date d=getDateValue(value,timeFormats,timeFormats);

+			TimeZone tz = TimeZone.getDefault();			

+			int dst = tz.getDSTSavings();	

+			Calendar c = Calendar.getInstance(tz);

+			c.setTime(d);

+			

+			if(timeFormats[0].equals("HH:mm:ssZ")&&(dst!=0))

+				c.add(Calendar.MILLISECOND,dst);

+			return new Long(

+					c.get(Calendar.HOUR_OF_DAY)*3600000

+					+c.get(Calendar.MINUTE)*60000

+					+c.get(Calendar.SECOND)*1000					

+			);

+		} else if (upnpType.equals("r4") || upnpType.equals("float")) {				

+			return new Float(value);

+		} else if (upnpType.equals("r8") || upnpType.equals("number")

+			|| upnpType.equals("fixed.14.4")){			

+			return new Double(value);

+		} else if (upnpType.equals("char")) {			

+			return new Character(value.charAt(0));

+		} else if (upnpType.equals("string") || upnpType.equals("uri")

+				|| upnpType.equals("uuid")) {			

+			return value;

+		} else if (upnpType.equals("date")) {

+			String[] timeFormats=new String[]{"yyyy-MM-dd"};

+			

+			Date d=getDateValue(value,timeFormats,timeFormats);

+			return d;			

+		} else if (upnpType.equals("dateTime")) {

+			

+			String[] timeFormats=new String[]{

+					"yyyy-MM-dd",

+					"yyyy-MM-dd'T'HH:mm:ss"

+			};

+			

+			Date d=getDateValue(value,timeFormats,timeFormats);

+			return d;

+		} else if (upnpType.equals("dateTime.tz")) {

+			

+			String[] timeFormats=new String[]{

+					"yyyy-MM-dd",

+					"yyyy-MM-dd'T'HH:mm:ss",

+					"yyyy-MM-dd'T'HH:mm:ssZ"

+			};

+			

+			Date d=getDateValue(value,timeFormats,timeFormats);

+			return d;			

+		} else if (upnpType.equals("boolean")) {

+			if(value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true")

+			|| value.equalsIgnoreCase("1"))

+				return new Boolean(true);

+			else

+				return new Boolean(false);					

+		} else if (upnpType.equals("bin.base64")) {

+			return Base64.decode(value);

+		} else if (upnpType.equals("bin.hex")) {

+			return HexBin.decode(value);

+		}

+		throw new IllegalArgumentException("Invalid Binding");		

+	}

+	

+	private static String normalizeTimeZone(String value){

+		if(value.endsWith("Z")){

+			value=value.substring(0,value.length()-1)+"+0000";

+		}else if((value.length()>7)

+			&&(value.charAt(value.length()-3)==':')

+			&&((value.charAt(value.length()-6)=='-')||(value.charAt(value.length()-6)=='+'))){

+			

+			value=value.substring(0,value.length()-3)+value.substring(value.length()-2);

+		}		

+		return value;

+	}

+	

+	/**

+	 * @param value

+	 * @param timeFormats

+	 * @param choosedIndex

+	 * @return

+	 * @throws ParseException

+	 */

+	private static Date getDateValue(String value, String[] timeFormats, String[] choosedIndex) throws ParseException {

+		ParsePosition position = null;

+		Date d;

+		value=normalizeTimeZone(value);

+		for (int i=0; i<timeFormats.length; i++) {

+			position =  new ParsePosition(0);

+			SimpleDateFormat  sdt = new SimpleDateFormat(timeFormats[i]);

+			d=sdt.parse(value,position);

+			if(d!=null){

+				if(position.getIndex()>=value.length()){

+					choosedIndex[0]=timeFormats[i];

+					return d;			

+				}

+			}

+		}

+		throw new ParseException("Error parsing "+value,position.getIndex());

+	}

+}

diff --git a/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/UPnPException.java b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/UPnPException.java
new file mode 100644
index 0000000..9807ba6
--- /dev/null
+++ b/org.apache.felix.upnp.extra/src/main/java/org/apache/felix/upnp/extra/util/UPnPException.java
@@ -0,0 +1,52 @@
+/*
+ DomoWare UPnP Base Driver is an implementation of the OSGi UnP Device Spcifaction
+ Copyright (C) 2004  Matteo Demuru, Francesco Furfari, Stefano "Kismet" Lenzi
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ You can contact us at:
+ {matte-d, sygent, kismet-sl} [at] users.sourceforge.net
+ */
+package org.apache.felix.upnp.extra.util;
+
+/**
+ * @author Stefano "Kismet" Lenzi
+ * @author Francesco Furfari
+ * 
+ */
+public class UPnPException extends Exception {
+
+	private int errorCode;
+
+	public UPnPException(int errorCode, String errorDescription,
+			Throwable t) {
+		super(errorDescription, t);
+		this.errorCode = errorCode;
+	}
+
+	public UPnPException(int errorCode, String errorDescription) {
+		super(errorDescription);
+		this.errorCode = errorCode;
+	}
+
+	public String getErrorDescription() {
+		return getMessage();
+	}
+
+	public int getErrorCode() {
+		return errorCode;
+	}
+
+}
diff --git a/org.apache.felix.upnp.extra/src/main/java/org/apache/xerces/impl/dv/util/Base64.java b/org.apache.felix.upnp.extra/src/main/java/org/apache/xerces/impl/dv/util/Base64.java
new file mode 100644
index 0000000..bd01597
--- /dev/null
+++ b/org.apache.felix.upnp.extra/src/main/java/org/apache/xerces/impl/dv/util/Base64.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * 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.apache.xerces.impl.dv.util;
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as
+ * defined by RFC 2045, N. Freed and N. Borenstein.
+ * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
+ * Part One: Format of Internet Message Bodies. Reference
+ * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
+ * This class is used by XML Schema binary format validation
+ *
+ * This implementation does not encode/decode streaming
+ * data. You need the data that you will encode/decode
+ * already on a byte arrray.
+ *
+ * @xerces.internal 
+ *
+ * @author Jeffrey Rodriguez
+ * @author Sandy Gao
+ * @version $Id: Base64.java,v 1.15 2004/10/14 15:20:18 mrglavas Exp $
+ */
+public final class  Base64 {
+
+    static private final int  BASELENGTH         = 128;
+    static private final int  LOOKUPLENGTH       = 64;
+    static private final int  TWENTYFOURBITGROUP = 24;
+    static private final int  EIGHTBIT           = 8;
+    static private final int  SIXTEENBIT         = 16;
+    static private final int  SIXBIT             = 6;
+    static private final int  FOURBYTE           = 4;
+    static private final int  SIGN               = -128;
+    static private final char PAD                = '=';
+    static private final boolean fDebug          = false;
+    static final private byte [] base64Alphabet        = new byte[BASELENGTH];
+    static final private char [] lookUpBase64Alphabet  = new char[LOOKUPLENGTH];
+
+    static {
+
+        for (int i = 0; i < BASELENGTH; ++i) {
+            base64Alphabet[i] = -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--) {
+            base64Alphabet[i] = (byte) (i-'A');
+        }
+        for (int i = 'z'; i>= 'a'; i--) {
+            base64Alphabet[i] = (byte) ( i-'a' + 26);
+        }
+
+        for (int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i-'0' + 52);
+        }
+
+        base64Alphabet['+']  = 62;
+        base64Alphabet['/']  = 63;
+
+        for (int i = 0; i<=25; i++)
+            lookUpBase64Alphabet[i] = (char)('A'+i);
+
+        for (int i = 26,  j = 0; i<=51; i++, j++)
+            lookUpBase64Alphabet[i] = (char)('a'+ j);
+
+        for (int i = 52,  j = 0; i<=61; i++, j++)
+            lookUpBase64Alphabet[i] = (char)('0' + j);
+        lookUpBase64Alphabet[62] = (char)'+';
+        lookUpBase64Alphabet[63] = (char)'/';
+
+    }
+
+    protected static boolean isWhiteSpace(char octect) {
+        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
+    }
+
+    protected static boolean isPad(char octect) {
+        return (octect == PAD);
+    }
+
+    protected static boolean isData(char octect) {
+        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
+    }
+
+    protected static boolean isBase64(char octect) {
+        return (isWhiteSpace(octect) || isPad(octect) || isData(octect));
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static String encode(byte[] binaryData) {
+
+        if (binaryData == null)
+            return null;
+
+        int      lengthDataBits    = binaryData.length*EIGHTBIT;
+        if (lengthDataBits == 0) {
+            return "";
+        }
+        
+        int      fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
+        int      numberTriplets    = lengthDataBits/TWENTYFOURBITGROUP;
+        int      numberQuartet     = fewerThan24bits != 0 ? numberTriplets+1 : numberTriplets;
+        char     encodedData[]     = null;
+
+        encodedData = new char[numberQuartet*4];
+
+        byte k=0, l=0, b1=0,b2=0,b3=0;
+
+        int encodedIndex = 0;
+        int dataIndex   = 0;
+        if (fDebug) {
+            System.out.println("number of triplets = " + numberTriplets );
+        }
+
+        for (int i=0; i<numberTriplets; i++) {
+            b1 = binaryData[dataIndex++];
+            b2 = binaryData[dataIndex++];
+            b3 = binaryData[dataIndex++];
+
+            if (fDebug) {
+                System.out.println( "b1= " + b1 +", b2= " + b2 + ", b3= " + b3 );
+            }
+
+            l  = (byte)(b2 & 0x0f);
+            k  = (byte)(b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+
+            byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
+            byte val3 = ((b3 & SIGN)==0)?(byte)(b3>>6):(byte)((b3)>>6^0xfc);
+
+            if (fDebug) {
+                System.out.println( "val2 = " + val2 );
+                System.out.println( "k4   = " + (k<<4));
+                System.out.println( "vak  = " + (val2 | (k<<4)));
+            }
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ val1 ];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ val2 | ( k<<4 )];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ (l <<2 ) | val3 ];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ b3 & 0x3f ];
+        }
+
+        // form integral number of 6-bit groups
+        if (fewerThan24bits == EIGHTBIT) {
+            b1 = binaryData[dataIndex];
+            k = (byte) ( b1 &0x03 );
+            if (fDebug) {
+                System.out.println("b1=" + b1);
+                System.out.println("b1<<2 = " + (b1>>2) );
+            }
+            byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ val1 ];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ k<<4 ];
+            encodedData[encodedIndex++] = PAD;
+            encodedData[encodedIndex++] = PAD;
+        } else if (fewerThan24bits == SIXTEENBIT) {
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex +1 ];
+            l = ( byte ) ( b2 &0x0f );
+            k = ( byte ) ( b1 &0x03 );
+
+            byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ val1 ];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ val2 | ( k<<4 )];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[ l<<2 ];
+            encodedData[encodedIndex++] = PAD;
+        }
+
+        return new String(encodedData);
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param encoded string containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode(String encoded) {
+
+        if (encoded == null)
+            return null;
+
+        char[] base64Data = encoded.toCharArray();
+        // remove white spaces
+        int len = removeWhiteSpace(base64Data);
+        
+        if (len%FOURBYTE != 0) {
+            return null;//should be divisible by four
+        }
+
+        int      numberQuadruple    = (len/FOURBYTE );
+
+        if (numberQuadruple == 0)
+            return new byte[0];
+
+        byte     decodedData[]      = null;
+        byte     b1=0,b2=0,b3=0,b4=0;
+        char     d1=0,d2=0,d3=0,d4=0;
+
+        int i = 0;
+        int encodedIndex = 0;
+        int dataIndex    = 0;
+        decodedData      = new byte[ (numberQuadruple)*3];
+
+        for (; i<numberQuadruple-1; i++) {
+
+            if (!isData( (d1 = base64Data[dataIndex++]) )||
+                !isData( (d2 = base64Data[dataIndex++]) )||
+                !isData( (d3 = base64Data[dataIndex++]) )||
+                !isData( (d4 = base64Data[dataIndex++]) ))
+                return null;//if found "no data" just return null
+
+            b1 = base64Alphabet[d1];
+            b2 = base64Alphabet[d2];
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+
+            decodedData[encodedIndex++] = (byte)(  b1 <<2 | b2>>4 ) ;
+            decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
+            decodedData[encodedIndex++] = (byte)( b3<<6 | b4 );
+        }
+
+        if (!isData( (d1 = base64Data[dataIndex++]) ) ||
+            !isData( (d2 = base64Data[dataIndex++]) )) {
+            return null;//if found "no data" just return null
+        }
+
+        b1 = base64Alphabet[d1];
+        b2 = base64Alphabet[d2];
+
+        d3 = base64Data[dataIndex++];
+        d4 = base64Data[dataIndex++];
+        if (!isData( (d3 ) ) ||
+            !isData( (d4 ) )) {//Check if they are PAD characters
+            if (isPad( d3 ) && isPad( d4)) {               //Two PAD e.g. 3c[Pad][Pad]
+                if ((b2 & 0xf) != 0)//last 4 bits should be zero
+                    return null;
+                byte[] tmp = new byte[ i*3 + 1 ];
+                System.arraycopy( decodedData, 0, tmp, 0, i*3 );
+                tmp[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                return tmp;
+            } else if (!isPad( d3) && isPad(d4)) {               //One PAD  e.g. 3cQ[Pad]
+                b3 = base64Alphabet[ d3 ];
+                if ((b3 & 0x3 ) != 0)//last 2 bits should be zero
+                    return null;
+                byte[] tmp = new byte[ i*3 + 2 ];
+                System.arraycopy( decodedData, 0, tmp, 0, i*3 );
+                tmp[encodedIndex++] = (byte)(  b1 <<2 | b2>>4 );
+                tmp[encodedIndex]   = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
+                return tmp;
+            } else {
+                return null;//an error  like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data
+            }
+        } else { //No PAD e.g 3cQl
+            b3 = base64Alphabet[ d3 ];
+            b4 = base64Alphabet[ d4 ];
+            decodedData[encodedIndex++] = (byte)(  b1 <<2 | b2>>4 ) ;
+            decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
+            decodedData[encodedIndex++] = (byte)( b3<<6 | b4 );
+
+        }
+
+        return decodedData;
+    }
+
+    /**
+     * remove WhiteSpace from MIME containing encoded Base64 data.
+     * 
+     * @param data  the byte array of base64 data (with WS)
+     * @return      the new length
+     */
+    protected static int removeWhiteSpace(char[] data) {
+        if (data == null)
+            return 0;
+
+        // count characters that's not whitespace
+        int newSize = 0;
+        int len = data.length;
+        for (int i = 0; i < len; i++) {
+            if (!isWhiteSpace(data[i]))
+                data[newSize++] = data[i];
+        }
+        return newSize;
+    }
+}
diff --git a/org.apache.felix.upnp.extra/src/main/java/org/apache/xerces/impl/dv/util/HexBin.java b/org.apache.felix.upnp.extra/src/main/java/org/apache/xerces/impl/dv/util/HexBin.java
new file mode 100644
index 0000000..0a3b3a3
--- /dev/null
+++ b/org.apache.felix.upnp.extra/src/main/java/org/apache/xerces/impl/dv/util/HexBin.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * 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.apache.xerces.impl.dv.util;
+
+/**
+ * format validation
+ *
+ * This class encodes/decodes hexadecimal data
+ * 
+ * @xerces.internal  
+ * 
+ * @author Jeffrey Rodriguez
+ * @version $Id: HexBin.java,v 1.13 2004/10/14 15:20:18 mrglavas Exp $
+ */
+public final class  HexBin {
+    static private final int  BASELENGTH   = 128;
+    static private final int  LOOKUPLENGTH = 16;
+    static final private byte [] hexNumberTable    = new byte[BASELENGTH];
+    static final private char [] lookUpHexAlphabet = new char[LOOKUPLENGTH];
+
+
+    static {
+        for (int i = 0; i < BASELENGTH; i++ ) {
+            hexNumberTable[i] = -1;
+        }
+        for ( int i = '9'; i >= '0'; i--) {
+            hexNumberTable[i] = (byte) (i-'0');
+        }
+        for ( int i = 'F'; i>= 'A'; i--) {
+            hexNumberTable[i] = (byte) ( i-'A' + 10 );
+        }
+        for ( int i = 'f'; i>= 'a'; i--) {
+           hexNumberTable[i] = (byte) ( i-'a' + 10 );
+        }
+
+        for(int i = 0; i<10; i++ ) {
+            lookUpHexAlphabet[i] = (char)('0'+i);
+        }
+        for(int i = 10; i<=15; i++ ) {
+            lookUpHexAlphabet[i] = (char)('A'+i -10);
+        }
+    }
+
+    /**
+     * Encode a byte array to hex string
+     *
+     * @param binaryData array of byte to encode
+     * @return return encoded string
+     */
+    static public String encode(byte[] binaryData) {
+        if (binaryData == null)
+            return null;
+        int lengthData   = binaryData.length;
+        int lengthEncode = lengthData * 2;
+        char[] encodedData = new char[lengthEncode];
+        int temp;
+        for (int i = 0; i < lengthData; i++) {
+            temp = binaryData[i];
+            if (temp < 0)
+                temp += 256;
+            encodedData[i*2] = lookUpHexAlphabet[temp >> 4];
+            encodedData[i*2+1] = lookUpHexAlphabet[temp & 0xf];
+        }
+        return new String(encodedData);
+    }
+
+    /**
+     * Decode hex string to a byte array
+     *
+     * @param encoded encoded string
+     * @return return array of byte to encode
+     */
+    static public byte[] decode(String encoded) {
+        if (encoded == null)
+            return null;
+        int lengthData = encoded.length();
+        if (lengthData % 2 != 0)
+            return null;
+
+        char[] binaryData = encoded.toCharArray();
+        int lengthDecode = lengthData / 2;
+        byte[] decodedData = new byte[lengthDecode];
+        byte temp1, temp2;
+        char tempChar;
+        for( int i = 0; i<lengthDecode; i++ ){
+            tempChar = binaryData[i*2];
+            temp1 = (tempChar < BASELENGTH) ? hexNumberTable[tempChar] : -1;
+            if (temp1 == -1)
+                return null;
+            tempChar = binaryData[i*2+1];
+            temp2 = (tempChar < BASELENGTH) ? hexNumberTable[tempChar] : -1;
+            if (temp2 == -1)
+                return null;
+            decodedData[i] = (byte)((temp1 << 4) | temp2);
+        }
+        return decodedData;
+    }
+}