From 02f0416b90b77337c546cae423ccab90a2e9adb6 Mon Sep 17 00:00:00 2001
From: marvin <marvin@marvin-TERRA-MOBILE-1529H>
Date: Tue, 3 Mar 2020 04:58:41 +0100
Subject: [PATCH] Adapter edits <xacro:include> tag in .urdf.xacro;

Output as bot.urdf.xacro
---
 build/sim/bot.urdf.xacro                      | 205 ++++++++++++++++++
 build/sim/botname.txt                         |   2 +-
 .../FeatureModelConfigurator/adapter.config   |  17 +-
 .../configs/config.xml                        |  16 --
 .../FeatureModelConfigurator/configs/test.xml |   9 +-
 .../features/ConfiguratorMain/Adapter.java    |  86 ++++++--
 .../features/ConfiguratorMain/XMLHelper.java  | 155 ++++++++++++-
 .../Configurator.jak                          |   2 +-
 .../Configurator.jak                          |   9 +
 .../Configurator.jak                          |   2 +-
 .../Configurator.jak                          |   2 +-
 .../Configurator.jak                          |   2 +-
 src/var/FeatureModelConfigurator/model.xml    |   9 +-
 .../resources/output/featuremodel.xml         |   2 +-
 .../FeatureModelConfigurator/src/Adapter.java |  86 ++++++--
 .../src/Configurator.jak                      |   8 +-
 .../src/Configurator.java                     |   6 +-
 .../src/XMLHelper.java                        | 155 ++++++++++++-
 .../modelsetup.config                         |   2 +-
 .../src/Adapter.java                          |  86 ++++++--
 .../src/ModelSetup.java                       |  26 +--
 .../src/XMLHelper.java                        | 155 ++++++++++++-
 22 files changed, 919 insertions(+), 123 deletions(-)
 create mode 100644 build/sim/bot.urdf.xacro
 delete mode 100644 src/var/FeatureModelConfigurator/configs/config.xml
 rename src/var/FeatureModelConfigurator/features/{WafflePi => turtlebot3_burger}/Configurator.jak (69%)
 create mode 100644 src/var/FeatureModelConfigurator/features/turtlebot3_burger_for_autorace/Configurator.jak
 rename src/var/FeatureModelConfigurator/features/{Waffle => turtlebot3_burgertest1}/Configurator.jak (67%)
 rename src/var/FeatureModelConfigurator/features/{Burger => turtlebot3_waffle}/Configurator.jak (69%)
 rename src/var/FeatureModelConfigurator/features/{BurgerForAutorace => turtlebot3_waffle_pi}/Configurator.jak (67%)

diff --git a/build/sim/bot.urdf.xacro b/build/sim/bot.urdf.xacro
new file mode 100644
index 0000000..0e9289f
--- /dev/null
+++ b/build/sim/bot.urdf.xacro
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<robot xmlns:xacro="http://ros.org/wiki/xacro" name="turtlebot3_waffle">
+  <xacro:include filename="$(find turtlebot3_description)/urdf/common_properties.xacro" />
+  <xacro:include filename="$(find sim)/bot.gazebo.xacro" />
+  <xacro:property name="r200_cam_rgb_px" value="0.005" />
+  <xacro:property name="r200_cam_rgb_py" value="0.018" />
+  <xacro:property name="r200_cam_rgb_pz" value="0.013" />
+  <xacro:property name="r200_cam_depth_offset" value="0.01" />
+  <link name="base_footprint" />
+  <joint name="base_joint" type="fixed">
+    <parent link="base_footprint" />
+    <child link="base_link" />
+    <origin xyz="0 0 0.010" rpy="0 0 0" />
+  </joint>
+  <link name="base_link">
+    <visual>
+      <origin xyz="-0.064 0 0.0" rpy="0 0 0" />
+      <geometry>
+        <mesh filename="package://turtlebot3_description/meshes/bases/waffle_base.stl" scale="0.001 0.001 0.001" />
+      </geometry>
+      <material name="light_black" />
+    </visual>
+    <collision>
+      <origin xyz="-0.064 0 0.047" rpy="0 0 0" />
+      <geometry>
+        <box size="0.266 0.266 0.094" />
+      </geometry>
+    </collision>
+    <inertial>
+      <origin xyz="0 0 0" rpy="0 0 0" />
+      <mass value="1.3729096e+00" />
+      <inertia ixx="8.7002718e-03" ixy="-4.7576583e-05" ixz="1.1160499e-04" iyy="8.6195418e-03" iyz="-3.5422299e-06" izz="1.4612727e-02" />
+    </inertial>
+  </link>
+  <joint name="wheel_left_joint" type="continuous">
+    <parent link="base_link" />
+    <child link="wheel_left_link" />
+    <origin xyz="0.0 0.144 0.023" rpy="-1.57 0 0" />
+    <axis xyz="0 0 1" />
+  </joint>
+  <link name="wheel_left_link">
+    <visual>
+      <origin xyz="0 0 0" rpy="1.57 0 0" />
+      <geometry>
+        <mesh filename="package://turtlebot3_description/meshes/wheels/left_tire.stl" scale="0.001 0.001 0.001" />
+      </geometry>
+      <material name="dark" />
+    </visual>
+    <collision>
+      <origin xyz="0 0 0" rpy="0 0 0" />
+      <geometry>
+        <cylinder length="0.018" radius="0.033" />
+      </geometry>
+    </collision>
+    <inertial>
+      <origin xyz="0 0 0" />
+      <mass value="2.8498940e-02" />
+      <inertia ixx="1.1175580e-05" ixy="-4.2369783e-11" ixz="-5.9381719e-09" iyy="1.1192413e-05" iyz="-1.4400107e-11" izz="2.0712558e-05" />
+    </inertial>
+  </link>
+  <joint name="wheel_right_joint" type="continuous">
+    <parent link="base_link" />
+    <child link="wheel_right_link" />
+    <origin xyz="0.0 -0.144 0.023" rpy="-1.57 0 0" />
+    <axis xyz="0 0 1" />
+  </joint>
+  <link name="wheel_right_link">
+    <visual>
+      <origin xyz="0 0 0" rpy="1.57 0 0" />
+      <geometry>
+        <mesh filename="package://turtlebot3_description/meshes/wheels/right_tire.stl" scale="0.001 0.001 0.001" />
+      </geometry>
+      <material name="dark" />
+    </visual>
+    <collision>
+      <origin xyz="0 0 0" rpy="0 0 0" />
+      <geometry>
+        <cylinder length="0.018" radius="0.033" />
+      </geometry>
+    </collision>
+    <inertial>
+      <origin xyz="0 0 0" />
+      <mass value="2.8498940e-02" />
+      <inertia ixx="1.1175580e-05" ixy="-4.2369783e-11" ixz="-5.9381719e-09" iyy="1.1192413e-05" iyz="-1.4400107e-11" izz="2.0712558e-05" />
+    </inertial>
+  </link>
+  <joint name="caster_back_right_joint" type="fixed">
+    <parent link="base_link" />
+    <child link="caster_back_right_link" />
+    <origin xyz="-0.177 -0.064 -0.004" rpy="-1.57 0 0" />
+  </joint>
+  <link name="caster_back_right_link">
+    <collision>
+      <origin xyz="0 0.001 0" rpy="0 0 0" />
+      <geometry>
+        <box size="0.030 0.009 0.020" />
+      </geometry>
+    </collision>
+    <inertial>
+      <origin xyz="0 0 0" />
+      <mass value="0.005" />
+      <inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.001" />
+    </inertial>
+  </link>
+  <joint name="caster_back_left_joint" type="fixed">
+    <parent link="base_link" />
+    <child link="caster_back_left_link" />
+    <origin xyz="-0.177 0.064 -0.004" rpy="-1.57 0 0" />
+  </joint>
+  <link name="caster_back_left_link">
+    <collision>
+      <origin xyz="0 0.001 0" rpy="0 0 0" />
+      <geometry>
+        <box size="0.030 0.009 0.020" />
+      </geometry>
+    </collision>
+    <inertial>
+      <origin xyz="0 0 0" />
+      <mass value="0.005" />
+      <inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.001" />
+    </inertial>
+  </link>
+  <joint name="imu_joint" type="fixed">
+    <parent link="base_link" />
+    <child link="imu_link" />
+    <origin xyz="0.0 0 0.068" rpy="0 0 0" />
+  </joint>
+  <link name="imu_link" />
+  <joint name="scan_joint" type="fixed">
+    <parent link="base_link" />
+    <child link="base_scan" />
+    <origin xyz="-0.064 0 0.122" rpy="0 0 0" />
+  </joint>
+  <link name="base_scan">
+    <visual>
+      <origin xyz="0 0 0" rpy="0 0 0" />
+      <geometry>
+        <mesh filename="package://turtlebot3_description/meshes/sensors/lds.stl" scale="0.001 0.001 0.001" />
+      </geometry>
+      <material name="dark" />
+    </visual>
+    <collision>
+      <origin xyz="0.015 0 -0.0065" rpy="0 0 0" />
+      <geometry>
+        <cylinder length="0.0315" radius="0.055" />
+      </geometry>
+    </collision>
+    <inertial>
+      <mass value="0.114" />
+      <origin xyz="0 0 0" />
+      <inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.001" />
+    </inertial>
+  </link>
+  <joint name="camera_joint" type="fixed">
+    <origin xyz="0.064 -0.065 0.094" rpy="0 0 0" />
+    <parent link="base_link" />
+    <child link="camera_link" />
+  </joint>
+  <link name="camera_link">
+    <visual>
+      <origin xyz="0 0 0" rpy="1.57 0 1.57" />
+      <geometry>
+        <mesh filename="package://turtlebot3_description/meshes/sensors/r200.dae" />
+      </geometry>
+    </visual>
+    <collision>
+      <origin xyz="0.003 0.065 0.007" rpy="0 0 0" />
+      <geometry>
+        <box size="0.012 0.132 0.020" />
+      </geometry>
+    </collision>
+    <!-- This inertial field needs doesn't contain reliable data!! -->
+    <!--   <inertial>
+      <mass value="0.564" />
+      <origin xyz="0 0 0" />
+      <inertia ixx="0.003881243" ixy="0.0" ixz="0.0"
+               iyy="0.000498940" iyz="0.0"
+               izz="0.003879257" />
+    </inertial>-->
+  </link>
+  <joint name="camera_rgb_joint" type="fixed">
+    <origin xyz="${r200_cam_rgb_px} ${r200_cam_rgb_py} ${r200_cam_rgb_pz}" rpy="0 0 0" />
+    <parent link="camera_link" />
+    <child link="camera_rgb_frame" />
+  </joint>
+  <link name="camera_rgb_frame" />
+  <joint name="camera_rgb_optical_joint" type="fixed">
+    <origin xyz="0 0 0" rpy="-1.57 0 -1.57" />
+    <parent link="camera_rgb_frame" />
+    <child link="camera_rgb_optical_frame" />
+  </joint>
+  <link name="camera_rgb_optical_frame" />
+  <joint name="camera_depth_joint" type="fixed">
+    <origin xyz="${r200_cam_rgb_px} ${r200_cam_rgb_py + r200_cam_depth_offset} ${r200_cam_rgb_pz}" rpy="0 0 0" />
+    <parent link="camera_link" />
+    <child link="camera_depth_frame" />
+  </joint>
+  <link name="camera_depth_frame" />
+  <joint name="camera_depth_optical_joint" type="fixed">
+    <origin xyz="0 0 0" rpy="-1.57 0 -1.57" />
+    <parent link="camera_depth_frame" />
+    <child link="camera_depth_optical_frame" />
+  </joint>
+  <link name="camera_depth_optical_frame" />
+</robot>
diff --git a/build/sim/botname.txt b/build/sim/botname.txt
index 82270b4..b49bf69 100644
--- a/build/sim/botname.txt
+++ b/build/sim/botname.txt
@@ -1 +1 @@
-Waffle
\ No newline at end of file
+turtlebot3_waffle
\ No newline at end of file
diff --git a/src/var/FeatureModelConfigurator/adapter.config b/src/var/FeatureModelConfigurator/adapter.config
index f0a7150..bc1d245 100644
--- a/src/var/FeatureModelConfigurator/adapter.config
+++ b/src/var/FeatureModelConfigurator/adapter.config
@@ -8,20 +8,13 @@
 		<!-- Path to all available sensor configuration files. -->
 		<!-- Files with other endings are allowed and will be ignored, unless this path is also used by ModelSetup (see ModelSetup) -->
 		<sensorconfig type="directory" path="./resources/input/sensorconfig/" />
-		<!-- Path to all available .gazebo.xacro files. -->
+		<!-- Path to all available .gazebo.xacro/ .urdf.xacro files. -->
 		<!-- Files with other endings are allowed and will be ignored. -->
-		<gazebomodel type="directory" path="./../../sim/turtlebot3/turtlebot3/turtlebot3_description/urdf_raw_for_testing/" />
-			<!-- <gazebomodel type="directory" path="./resources/input/bot_description/" /> -->
-		<!-- Path to all available .urdf.xacro files. -->
-		<!-- Files with other endings are allowed and will be ignored. -->
-		<urdfmodel type="directory" path="./../../sim/turtlebot3/turtlebot3/turtlebot3_description/urdf_raw_for_testing/" />
+		<bot_description type="directory" path="./../../sim/turtlebot3/turtlebot3/turtlebot3_description/urdf_raw_for_testing/" />
 	</input>
 	<output>
-		<!-- Path to and name of the generated .gazebo.xacro file. -->
-		<gazebomodel type="file" name="bot.gazebo.xacro" path="./../../../build/sim/" />
-		<!-- Path to and name of the generated .urdf.xacro file. -->
-		<urdfmodel type="file" name="bot.urdf.xacro" path="./../../../build/sim/" />
-		<!-- Path to and name of the file stating the selected bots name. -->
-		<botname type="file" name="botname.txt" path="./../../../build/sim/" />
+		<!-- Path to and name of the generated .gazebo.xacro/ .urdf.xacro/ file stating the selected bots name. -->
+		<bot_description type="file" gazebo_xacro="bot.gazebo.xacro" urdf_xacro="bot.urdf.xacro" botname="botname.txt" path="./../../../build/sim/" />
+		<botname /><!-- TODO -->
 	</output>
 </adapter>
diff --git a/src/var/FeatureModelConfigurator/configs/config.xml b/src/var/FeatureModelConfigurator/configs/config.xml
deleted file mode 100644
index 139abd4..0000000
--- a/src/var/FeatureModelConfigurator/configs/config.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<configuration>
-	<feature automatic="selected" name="ConfiguratorMain"/>
-	<feature automatic="selected" name="Templates"/>
-	<feature automatic="unselected" name="Burger"/>
-	<feature automatic="unselected" name="BurgerForAutorace"/>
-	<feature automatic="unselected" name="Waffle"/>
-	<feature manual="selected" name="WafflePi"/>
-	<feature automatic="selected" name="Sensors"/>
-	<feature automatic="unselected" name="NO_CHANGE"/>
-	<feature manual="selected" name="Custom"/>
-	<feature name="Pi_Camera"/>
-	<feature name="imu"/>
-	<feature name="lds_lfcd_sensor"/>
-	<feature name="realsense_R200"/>
-</configuration>
diff --git a/src/var/FeatureModelConfigurator/configs/test.xml b/src/var/FeatureModelConfigurator/configs/test.xml
index 553cef5..2346818 100644
--- a/src/var/FeatureModelConfigurator/configs/test.xml
+++ b/src/var/FeatureModelConfigurator/configs/test.xml
@@ -2,10 +2,11 @@
 <configuration>
 	<feature automatic="selected" name="ConfiguratorMain"/>
 	<feature automatic="selected" name="Templates"/>
-	<feature automatic="unselected" name="Burger"/>
-	<feature automatic="unselected" name="BurgerForAutorace"/>
-	<feature manual="selected" name="Waffle"/>
-	<feature automatic="unselected" name="WafflePi"/>
+	<feature automatic="unselected" name="turtlebot3_burger"/>
+	<feature automatic="unselected" name="turtlebot3_burger_for_autorace"/>
+	<feature automatic="unselected" name="turtlebot3_burgertest1"/>
+	<feature manual="selected" name="turtlebot3_waffle"/>
+	<feature automatic="unselected" name="turtlebot3_waffle_pi"/>
 	<feature automatic="selected" name="Sensors"/>
 	<feature manual="selected" name="NO_CHANGE"/>
 	<feature automatic="unselected" name="Custom"/>
diff --git a/src/var/FeatureModelConfigurator/features/ConfiguratorMain/Adapter.java b/src/var/FeatureModelConfigurator/features/ConfiguratorMain/Adapter.java
index e4e3580..106cffe 100644
--- a/src/var/FeatureModelConfigurator/features/ConfiguratorMain/Adapter.java
+++ b/src/var/FeatureModelConfigurator/features/ConfiguratorMain/Adapter.java
@@ -6,8 +6,8 @@ import org.jdom2.*;
 
 
 /**
- * Takes a featuremodel.xml file generated using FeatureModelConfigurator and a given ROS/ Gazebo .gazebo.xacro file.
- * The .gazebo.xacro file is modified to support the features specified in the featuremodel.xml file.
+ * Takes a featuremodel.xml file generated using FeatureModelConfigurator and a given .gazebo.xacro and urdf.xacro file.
+ * The .gazebo.xacro and .urdf.xacro files are modified to support the features specified in the featuremodel.xml file.
  */
 public class Adapter extends XMLHelper {
 	
@@ -32,29 +32,33 @@ public class Adapter extends XMLHelper {
 	/**
 	 * Call this to run Adapter.
 	 */
+	//public static void main(String[] args) {
 	public Adapter() {
 		System.out.println("Adapter started.");
 		
 		try {
 			final String SC_ENDING = ".xml";
-			final String GM_ENDING = ".gazebo.xacro";
+			final String GX_ENDING = ".gazebo.xacro";
+			final String UX_ENDING = ".urdf.xacro";
 			
 			//######### Input ###############################			
 			Document adapterConfig = readXml("./adapter.config");
 			
 			String in_fmPath = getConfigVal(adapterConfig, "input/featuremodel", "path");
 			String in_scPath = getConfigVal(adapterConfig, "input/sensorconfig", "path");
-			String in_gmPath = getConfigVal(adapterConfig, "input/gazebomodel", "path");
-			String out_gmPath = getConfigVal(adapterConfig, "output/gazebomodel", "path");
-			String out_bnPath = getConfigVal(adapterConfig, "output/botname", "path");
+			String in_descPath = getConfigVal(adapterConfig, "input/bot_description", "path");
+			String out_descPath = getConfigVal(adapterConfig, "output/bot_description", "path");
 			String in_fmName = getConfigVal(adapterConfig, "input/featuremodel", "name");
-			String in_gmName = "";
-			String out_gmName = getConfigVal(adapterConfig, "output/gazebomodel", "name");
-			String out_bnName = getConfigVal(adapterConfig, "output/botname", "name");
+			String in_gxName = "";
+			String in_uxName = "";
+			String out_gxName = getConfigVal(adapterConfig, "output/bot_description", "gazebo_xacro");
+			String out_uxName = getConfigVal(adapterConfig, "output/bot_description", "urdf_xacro");
+			String out_bnName = getConfigVal(adapterConfig, "output/bot_description", "botname");
 			
 			Document featureModel = readXml(in_fmPath+in_fmName);
 			List<Document> sensorConfigList = new ArrayList<Document>();
 			Document gazeboModel = null;
+			Document urdfModel = null;
 			
 			//Get the selected mode (either NO_CHANGE or Custom) as String.
 			String mode = "";
@@ -106,19 +110,34 @@ public class Adapter extends XMLHelper {
 				System.exit(-1);
 			}
 
-			in_gmName = botName+GM_ENDING;
+			in_gxName = botName+GX_ENDING;
 			
-			List<String> gmList = getFileNames(in_gmPath);
+			List<String> gxList = getFileNames(in_descPath);
 			boolean inDirectory = false;
-			for(String fileName : gmList) {
-				if(fileName.equals(in_gmName)) inDirectory = true;
+			for(String fileName : gxList) {
+				if(fileName.equals(in_gxName)) inDirectory = true;
 			}
 			if(!inDirectory) {
-				System.err.println("Cannot handle \'"+botName+"\': No input file known.");
+				System.err.println("Cannot handle \'"+botName+"\': No \'"+GX_ENDING+"\' input file known.");
 				System.exit(-1);
 			}
 			
-			gazeboModel = readXml(in_gmPath+in_gmName);
+			gazeboModel = readXml(in_descPath+in_gxName);
+			
+			//Get the corresponding .urdf.xacro file as Document.
+			in_uxName = botName+UX_ENDING;
+			
+			List<String> urdfList = getFileNames(in_descPath);
+			inDirectory = false;
+			for(String fileName : urdfList) {
+				if(fileName.equals(in_uxName)) inDirectory = true;
+			}
+			if(!inDirectory) {
+				System.err.println("Cannot handle \'"+botName+"\': No \'"+UX_ENDING+"\' input file known.");
+				System.exit(-1);
+			}
+			
+			urdfModel = readXml(in_descPath+in_uxName);
 			//###############################################
 			
 			
@@ -136,7 +155,7 @@ public class Adapter extends XMLHelper {
 							entry.detach();
 						}
 						else {
-							System.out.println("Found a <sensor> element in \'"+in_gmName+
+							System.out.println("Found a <sensor> element in \'"+in_gxName+
 								"\' that does not have a <gazebo> element as immediate parent. It was not removed, as this input is unexpected.");	
 						}
 					}
@@ -173,10 +192,41 @@ public class Adapter extends XMLHelper {
 			//###############################################
 			
 			
+			//######### Operate on .urdf.xacro ############
+			final Namespace ns = urdfModel.getRootElement().getNamespace("xacro");
+			
+			//Set <xacro:include> tags
+			List<Element> includeList = xQueryElements(urdfModel, "//xacro:include", ns);
+			if(includeList.size()!=2) {
+				System.err.println("Invalid input in \'"+in_uxName+"\': Expected exactly 2 xacro:include tags.");
+				System.exit(-1);
+			}
+			String destDirectoryName = (new File(out_descPath)).getName();
+			//includeList.get(0).setAttribute("filename", "$(find "+destDirectoryName+")/common_properties.xacro");//TODO
+			includeList.get(1).setAttribute("filename", "$(find "+destDirectoryName+")/"+out_gxName);
+			
+			switch(mode) {
+				case "NO_CHANGE":
+					break;
+				case "Custom":
+					//TODO
+					//If the urdf.xacro would be modified (to have an accurate visual model with the sensors attached to it), 
+					//this needs to happen here. (Delete or Insert <link>/ <joint> tags.)
+					//This would require separate files with <link>/ <joint> tags for each bot for the Adapter to access...
+					break;
+				default:
+					System.err.println("Invalid input in \'"+in_fmName+"\': NO_CHANGE/ Custom parameter could not be parsed.");
+					System.exit(-1);
+			}
+			//###############################################
+			
+			
 			//######### Output result #######################
-			saveAsXml(gazeboModel, out_gmPath+out_gmName);
+			saveAsXml(gazeboModel, out_descPath+out_gxName);
+			
+			saveAsXml(urdfModel, out_descPath+out_uxName);
 			
-			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(out_bnPath+out_bnName));
+			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(out_descPath+out_bnName));
 			bufferedWriter.write(botName);
 			bufferedWriter.close();
 			//###############################################
diff --git a/src/var/FeatureModelConfigurator/features/ConfiguratorMain/XMLHelper.java b/src/var/FeatureModelConfigurator/features/ConfiguratorMain/XMLHelper.java
index 30d0f3b..3a1030b 100644
--- a/src/var/FeatureModelConfigurator/features/ConfiguratorMain/XMLHelper.java
+++ b/src/var/FeatureModelConfigurator/features/ConfiguratorMain/XMLHelper.java
@@ -57,13 +57,32 @@ public class XMLHelper {
 		File directory = new File(path);
 		File[] contentList = directory.listFiles();
 
-		for (File file : contentList) {
-			if (file.isFile()) fileNames.add(file.getName());
+		for (File elem : contentList) {
+			if (elem.isFile()) fileNames.add(elem.getName());
 		}
 		
 		return fileNames;
 	}
 	
+	/**
+	 * Takes a given directory path and lists all directories directly in it.
+	 * (Does not visit sub-directories recursively)
+	 * 
+	 * @param path			Path to the directory
+	 * @return ArrayList with all directory names
+	 */
+	public static ArrayList<String> getDirectoryNames(String path) {
+		ArrayList<String> directoryNames = new ArrayList<String>();
+		File directory = new File(path);
+		File[] contentList = directory.listFiles();
+
+		for (File elem : contentList) {
+			if (elem.isDirectory()) directoryNames.add(elem.getName());
+		}
+		
+		return directoryNames;
+	}
+	
 	/**
 	 * Executes given XPath expression for elements with JDOM Document as source.
 	 * 
@@ -79,6 +98,23 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for elements with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param xPath			XPath expression
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> xQueryElements(Document doc, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Element> expr = xFactory.compile(xPath, Filters.element(), null, ns);
+		List<Element> result = expr.evaluate(doc);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for elements with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -95,6 +131,24 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for elements with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param xPath			XPath expression
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> xQueryElements(Element elem, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Element> expr = xFactory.compile(xPath, Filters.element(), null, ns);
+		List<Element> result = expr.evaluate(elem);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for attributes with JDOM Document as source.
 	 * 
@@ -110,6 +164,23 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for attributes with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param xPath			XPath expression
+	 * @return List with all attributes found or empty list if no match found
+	 */
+	public static List<Attribute> xQueryAttributes(Document doc, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Attribute> expr = xFactory.compile(xPath, Filters.attribute(), null, ns);
+		List<Attribute> result = expr.evaluate(doc);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for attributes with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -126,6 +197,24 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for attributes with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param xPath			XPath expression
+	 * @return List with all attributes found or empty list if no match found
+	 */
+	public static List<Attribute> xQueryAttributes(Element elem, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Attribute> expr = xFactory.compile(xPath, Filters.attribute(), null, ns);
+		List<Attribute> result = expr.evaluate(elem);
+		
+		return result;
+	}
+	
 	/**
 	 * Searches a document for the first occurrence of an XML tag with JDOM Document as source.
 	 * 
@@ -141,6 +230,23 @@ public class XMLHelper {
 		return query.get(0);
 	}
 	
+	/**
+	 * Searches a document for the first occurrence of an XML tag with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param elementName	Name of the XML tag
+	 * @return First element found or NULL if no match found
+	 */
+	public static Element searchFirstElement(Document doc, String elementName, Namespace ns) {
+		//XPath index starts with 1; [] has a higher precedence than //
+		List<Element> query= xQueryElements(doc, "(//"+elementName+")[1]", ns);
+		
+		if(query.isEmpty()) return null;
+		return query.get(0);
+	}
+	
 	/**
 	 * Searches a document for the first occurrence of an XML tag with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -157,6 +263,24 @@ public class XMLHelper {
 		return query.get(0);
 	}
 	
+	/**
+	 * Searches a document for the first occurrence of an XML tag with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param elementName	Name of the XML tag
+	 * @return First element found or NULL if no match found
+	 */
+	public static Element searchFirstElement(Element elem, String elementName, Namespace ns) {
+		//XPath index starts with 1; [] has a higher precedence than //
+		List<Element> query= xQueryElements(elem, "(//"+elementName+")[1]", ns);
+		
+		if(query.isEmpty()) return null;
+		return query.get(0);
+	}
+	
 	/**
 	 * Searches a document for all occurrences of an XML tag with JDOM Document as source.
 	 * 
@@ -168,6 +292,19 @@ public class XMLHelper {
 		return xQueryElements(doc, "//"+elementName);
 	}
 	
+	/**
+	 * Searches a document for all occurrences of an XML tag with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param elementName	Name of the XML tag
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> searchAllElements(Document doc, String elementName, Namespace ns) {
+		return xQueryElements(doc, "//"+elementName, ns);
+	}
+	
 	/**
 	 * Searches a document for all occurrences of an XML tag with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -180,6 +317,20 @@ public class XMLHelper {
 		return xQueryElements(elem, "//"+elementName);
 	}
 	
+	/**
+	 * Searches a document for all occurrences of an XML tag with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param elementName	Name of the XML tag
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> searchAllElements(Element elem, String elementName, Namespace ns) {
+		return xQueryElements(elem, "//"+elementName, ns);
+	}
+	
 	/**
 	 * Checks whether two elements have an equal XML tag.
 	 * (Name of the XML tag and all attributes including their order must be equal)
diff --git a/src/var/FeatureModelConfigurator/features/WafflePi/Configurator.jak b/src/var/FeatureModelConfigurator/features/turtlebot3_burger/Configurator.jak
similarity index 69%
rename from src/var/FeatureModelConfigurator/features/WafflePi/Configurator.jak
rename to src/var/FeatureModelConfigurator/features/turtlebot3_burger/Configurator.jak
index f988df1..e3cc347 100644
--- a/src/var/FeatureModelConfigurator/features/WafflePi/Configurator.jak
+++ b/src/var/FeatureModelConfigurator/features/turtlebot3_burger/Configurator.jak
@@ -3,7 +3,7 @@
  */
 public refines class Configurator {
 	public void save() {
-		Super().addToModuleList("WafflePi");
+		Super().addToModuleList("turtlebot3_burger");
 		Super().save();
 	}
 }
\ No newline at end of file
diff --git a/src/var/FeatureModelConfigurator/features/turtlebot3_burger_for_autorace/Configurator.jak b/src/var/FeatureModelConfigurator/features/turtlebot3_burger_for_autorace/Configurator.jak
new file mode 100644
index 0000000..b459d76
--- /dev/null
+++ b/src/var/FeatureModelConfigurator/features/turtlebot3_burger_for_autorace/Configurator.jak
@@ -0,0 +1,9 @@
+/**
+ * TODO description
+ */
+public refines class Configurator {
+	public void save() {
+		Super().addToModuleList("turtlebot3_burger_for_autorace");
+		Super().save();
+	}
+}
\ No newline at end of file
diff --git a/src/var/FeatureModelConfigurator/features/Waffle/Configurator.jak b/src/var/FeatureModelConfigurator/features/turtlebot3_burgertest1/Configurator.jak
similarity index 67%
rename from src/var/FeatureModelConfigurator/features/Waffle/Configurator.jak
rename to src/var/FeatureModelConfigurator/features/turtlebot3_burgertest1/Configurator.jak
index 2a9c54e..3042e18 100644
--- a/src/var/FeatureModelConfigurator/features/Waffle/Configurator.jak
+++ b/src/var/FeatureModelConfigurator/features/turtlebot3_burgertest1/Configurator.jak
@@ -3,7 +3,7 @@
  */
 public refines class Configurator {
 	public void save() {
-		Super().addToModuleList("Waffle");
+		Super().addToModuleList("turtlebot3_burgertest1");
 		Super().save();
 	}
 }
\ No newline at end of file
diff --git a/src/var/FeatureModelConfigurator/features/Burger/Configurator.jak b/src/var/FeatureModelConfigurator/features/turtlebot3_waffle/Configurator.jak
similarity index 69%
rename from src/var/FeatureModelConfigurator/features/Burger/Configurator.jak
rename to src/var/FeatureModelConfigurator/features/turtlebot3_waffle/Configurator.jak
index a575616..067c472 100644
--- a/src/var/FeatureModelConfigurator/features/Burger/Configurator.jak
+++ b/src/var/FeatureModelConfigurator/features/turtlebot3_waffle/Configurator.jak
@@ -3,7 +3,7 @@
  */
 public refines class Configurator {
 	public void save() {
-		Super().addToModuleList("Burger");
+		Super().addToModuleList("turtlebot3_waffle");
 		Super().save();
 	}
 }
\ No newline at end of file
diff --git a/src/var/FeatureModelConfigurator/features/BurgerForAutorace/Configurator.jak b/src/var/FeatureModelConfigurator/features/turtlebot3_waffle_pi/Configurator.jak
similarity index 67%
rename from src/var/FeatureModelConfigurator/features/BurgerForAutorace/Configurator.jak
rename to src/var/FeatureModelConfigurator/features/turtlebot3_waffle_pi/Configurator.jak
index 3b83c07..cc6808b 100644
--- a/src/var/FeatureModelConfigurator/features/BurgerForAutorace/Configurator.jak
+++ b/src/var/FeatureModelConfigurator/features/turtlebot3_waffle_pi/Configurator.jak
@@ -3,7 +3,7 @@
  */
 public refines class Configurator {
 	public void save() {
-		Super().addToModuleList("BurgerForAutorace");
+		Super().addToModuleList("turtlebot3_waffle_pi");
 		Super().save();
 	}
 }
\ No newline at end of file
diff --git a/src/var/FeatureModelConfigurator/model.xml b/src/var/FeatureModelConfigurator/model.xml
index ddc54f1..c63c0f7 100644
--- a/src/var/FeatureModelConfigurator/model.xml
+++ b/src/var/FeatureModelConfigurator/model.xml
@@ -13,10 +13,11 @@
     <and mandatory="true" name="ConfiguratorMain">
       <alt abstract="true" mandatory="true" name="Templates">
         <!-- Bot template features go here-->
-        <feature name="Burger" />
-        <feature name="BurgerForAutorace" />
-        <feature name="Waffle" />
-        <feature name="WafflePi" />
+        <feature name="turtlebot3_burger" />
+        <feature name="turtlebot3_burger_for_autorace" />
+        <feature name="turtlebot3_burgertest1" />
+        <feature name="turtlebot3_waffle" />
+        <feature name="turtlebot3_waffle_pi" />
       </alt>
       <alt abstract="true" mandatory="true" name="Sensors">
         <feature name="NO_CHANGE" />
diff --git a/src/var/FeatureModelConfigurator/resources/output/featuremodel.xml b/src/var/FeatureModelConfigurator/resources/output/featuremodel.xml
index 0b5b671..a8bd65a 100644
--- a/src/var/FeatureModelConfigurator/resources/output/featuremodel.xml
+++ b/src/var/FeatureModelConfigurator/resources/output/featuremodel.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <specification>
-  <templates name="Waffle" />
+  <templates name="turtlebot3_waffle" />
   <sensors>
     <mode param="NO_CHANGE" />
   </sensors>
diff --git a/src/var/FeatureModelConfigurator/src/Adapter.java b/src/var/FeatureModelConfigurator/src/Adapter.java
index e4e3580..106cffe 100644
--- a/src/var/FeatureModelConfigurator/src/Adapter.java
+++ b/src/var/FeatureModelConfigurator/src/Adapter.java
@@ -6,8 +6,8 @@ import org.jdom2.*;
 
 
 /**
- * Takes a featuremodel.xml file generated using FeatureModelConfigurator and a given ROS/ Gazebo .gazebo.xacro file.
- * The .gazebo.xacro file is modified to support the features specified in the featuremodel.xml file.
+ * Takes a featuremodel.xml file generated using FeatureModelConfigurator and a given .gazebo.xacro and urdf.xacro file.
+ * The .gazebo.xacro and .urdf.xacro files are modified to support the features specified in the featuremodel.xml file.
  */
 public class Adapter extends XMLHelper {
 	
@@ -32,29 +32,33 @@ public class Adapter extends XMLHelper {
 	/**
 	 * Call this to run Adapter.
 	 */
+	//public static void main(String[] args) {
 	public Adapter() {
 		System.out.println("Adapter started.");
 		
 		try {
 			final String SC_ENDING = ".xml";
-			final String GM_ENDING = ".gazebo.xacro";
+			final String GX_ENDING = ".gazebo.xacro";
+			final String UX_ENDING = ".urdf.xacro";
 			
 			//######### Input ###############################			
 			Document adapterConfig = readXml("./adapter.config");
 			
 			String in_fmPath = getConfigVal(adapterConfig, "input/featuremodel", "path");
 			String in_scPath = getConfigVal(adapterConfig, "input/sensorconfig", "path");
-			String in_gmPath = getConfigVal(adapterConfig, "input/gazebomodel", "path");
-			String out_gmPath = getConfigVal(adapterConfig, "output/gazebomodel", "path");
-			String out_bnPath = getConfigVal(adapterConfig, "output/botname", "path");
+			String in_descPath = getConfigVal(adapterConfig, "input/bot_description", "path");
+			String out_descPath = getConfigVal(adapterConfig, "output/bot_description", "path");
 			String in_fmName = getConfigVal(adapterConfig, "input/featuremodel", "name");
-			String in_gmName = "";
-			String out_gmName = getConfigVal(adapterConfig, "output/gazebomodel", "name");
-			String out_bnName = getConfigVal(adapterConfig, "output/botname", "name");
+			String in_gxName = "";
+			String in_uxName = "";
+			String out_gxName = getConfigVal(adapterConfig, "output/bot_description", "gazebo_xacro");
+			String out_uxName = getConfigVal(adapterConfig, "output/bot_description", "urdf_xacro");
+			String out_bnName = getConfigVal(adapterConfig, "output/bot_description", "botname");
 			
 			Document featureModel = readXml(in_fmPath+in_fmName);
 			List<Document> sensorConfigList = new ArrayList<Document>();
 			Document gazeboModel = null;
+			Document urdfModel = null;
 			
 			//Get the selected mode (either NO_CHANGE or Custom) as String.
 			String mode = "";
@@ -106,19 +110,34 @@ public class Adapter extends XMLHelper {
 				System.exit(-1);
 			}
 
-			in_gmName = botName+GM_ENDING;
+			in_gxName = botName+GX_ENDING;
 			
-			List<String> gmList = getFileNames(in_gmPath);
+			List<String> gxList = getFileNames(in_descPath);
 			boolean inDirectory = false;
-			for(String fileName : gmList) {
-				if(fileName.equals(in_gmName)) inDirectory = true;
+			for(String fileName : gxList) {
+				if(fileName.equals(in_gxName)) inDirectory = true;
 			}
 			if(!inDirectory) {
-				System.err.println("Cannot handle \'"+botName+"\': No input file known.");
+				System.err.println("Cannot handle \'"+botName+"\': No \'"+GX_ENDING+"\' input file known.");
 				System.exit(-1);
 			}
 			
-			gazeboModel = readXml(in_gmPath+in_gmName);
+			gazeboModel = readXml(in_descPath+in_gxName);
+			
+			//Get the corresponding .urdf.xacro file as Document.
+			in_uxName = botName+UX_ENDING;
+			
+			List<String> urdfList = getFileNames(in_descPath);
+			inDirectory = false;
+			for(String fileName : urdfList) {
+				if(fileName.equals(in_uxName)) inDirectory = true;
+			}
+			if(!inDirectory) {
+				System.err.println("Cannot handle \'"+botName+"\': No \'"+UX_ENDING+"\' input file known.");
+				System.exit(-1);
+			}
+			
+			urdfModel = readXml(in_descPath+in_uxName);
 			//###############################################
 			
 			
@@ -136,7 +155,7 @@ public class Adapter extends XMLHelper {
 							entry.detach();
 						}
 						else {
-							System.out.println("Found a <sensor> element in \'"+in_gmName+
+							System.out.println("Found a <sensor> element in \'"+in_gxName+
 								"\' that does not have a <gazebo> element as immediate parent. It was not removed, as this input is unexpected.");	
 						}
 					}
@@ -173,10 +192,41 @@ public class Adapter extends XMLHelper {
 			//###############################################
 			
 			
+			//######### Operate on .urdf.xacro ############
+			final Namespace ns = urdfModel.getRootElement().getNamespace("xacro");
+			
+			//Set <xacro:include> tags
+			List<Element> includeList = xQueryElements(urdfModel, "//xacro:include", ns);
+			if(includeList.size()!=2) {
+				System.err.println("Invalid input in \'"+in_uxName+"\': Expected exactly 2 xacro:include tags.");
+				System.exit(-1);
+			}
+			String destDirectoryName = (new File(out_descPath)).getName();
+			//includeList.get(0).setAttribute("filename", "$(find "+destDirectoryName+")/common_properties.xacro");//TODO
+			includeList.get(1).setAttribute("filename", "$(find "+destDirectoryName+")/"+out_gxName);
+			
+			switch(mode) {
+				case "NO_CHANGE":
+					break;
+				case "Custom":
+					//TODO
+					//If the urdf.xacro would be modified (to have an accurate visual model with the sensors attached to it), 
+					//this needs to happen here. (Delete or Insert <link>/ <joint> tags.)
+					//This would require separate files with <link>/ <joint> tags for each bot for the Adapter to access...
+					break;
+				default:
+					System.err.println("Invalid input in \'"+in_fmName+"\': NO_CHANGE/ Custom parameter could not be parsed.");
+					System.exit(-1);
+			}
+			//###############################################
+			
+			
 			//######### Output result #######################
-			saveAsXml(gazeboModel, out_gmPath+out_gmName);
+			saveAsXml(gazeboModel, out_descPath+out_gxName);
+			
+			saveAsXml(urdfModel, out_descPath+out_uxName);
 			
-			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(out_bnPath+out_bnName));
+			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(out_descPath+out_bnName));
 			bufferedWriter.write(botName);
 			bufferedWriter.close();
 			//###############################################
diff --git a/src/var/FeatureModelConfigurator/src/Configurator.jak b/src/var/FeatureModelConfigurator/src/Configurator.jak
index 7b72c8d..9c9d5ad 100644
--- a/src/var/FeatureModelConfigurator/src/Configurator.jak
+++ b/src/var/FeatureModelConfigurator/src/Configurator.jak
@@ -176,12 +176,12 @@ abstract class Configurator$$ConfiguratorMain {
 	}
 }
 
-SoUrCe  Waffle "../features/Waffle/Configurator.jak";/**
+SoUrCe  turtlebot3_waffle "../features/turtlebot3_waffle/Configurator.jak";/**
  * TODO description
  */
-abstract class Configurator$$Waffle extends  Configurator$$ConfiguratorMain  {
+abstract class Configurator$$turtlebot3_waffle extends  Configurator$$ConfiguratorMain  {
 	public void save() {
-		Super().addToModuleList("Waffle");
+		Super().addToModuleList("turtlebot3_waffle");
 		Super().save();
 	}
 }
@@ -189,7 +189,7 @@ abstract class Configurator$$Waffle extends  Configurator$$ConfiguratorMain  {
 SoUrCe  NO_CHANGE "../features/NO_CHANGE/Configurator.jak";/**
  * TODO description
  */
-public class Configurator extends  Configurator$$Waffle  {
+public class Configurator extends  Configurator$$turtlebot3_waffle  {
 	public void save() {
 		Super().addToModuleList("NO_CHANGE");
 		Super().save();
diff --git a/src/var/FeatureModelConfigurator/src/Configurator.java b/src/var/FeatureModelConfigurator/src/Configurator.java
index 878c955..7301a67 100644
--- a/src/var/FeatureModelConfigurator/src/Configurator.java
+++ b/src/var/FeatureModelConfigurator/src/Configurator.java
@@ -179,9 +179,9 @@ abstract class Configurator$$ConfiguratorMain {
 /**
  * TODO description
  */
-abstract class Configurator$$Waffle extends  Configurator$$ConfiguratorMain  {
+abstract class Configurator$$turtlebot3_waffle extends  Configurator$$ConfiguratorMain  {
 	public void save() {
-		super.addToModuleList("Waffle");
+		super.addToModuleList("turtlebot3_waffle");
 		super.save();
 	}
 }
@@ -189,7 +189,7 @@ abstract class Configurator$$Waffle extends  Configurator$$ConfiguratorMain  {
 /**
  * TODO description
  */
-public class Configurator extends  Configurator$$Waffle  {
+public class Configurator extends  Configurator$$turtlebot3_waffle  {
 	public void save() {
 		super.addToModuleList("NO_CHANGE");
 		super.save();
diff --git a/src/var/FeatureModelConfigurator/src/XMLHelper.java b/src/var/FeatureModelConfigurator/src/XMLHelper.java
index 30d0f3b..3a1030b 100644
--- a/src/var/FeatureModelConfigurator/src/XMLHelper.java
+++ b/src/var/FeatureModelConfigurator/src/XMLHelper.java
@@ -57,13 +57,32 @@ public class XMLHelper {
 		File directory = new File(path);
 		File[] contentList = directory.listFiles();
 
-		for (File file : contentList) {
-			if (file.isFile()) fileNames.add(file.getName());
+		for (File elem : contentList) {
+			if (elem.isFile()) fileNames.add(elem.getName());
 		}
 		
 		return fileNames;
 	}
 	
+	/**
+	 * Takes a given directory path and lists all directories directly in it.
+	 * (Does not visit sub-directories recursively)
+	 * 
+	 * @param path			Path to the directory
+	 * @return ArrayList with all directory names
+	 */
+	public static ArrayList<String> getDirectoryNames(String path) {
+		ArrayList<String> directoryNames = new ArrayList<String>();
+		File directory = new File(path);
+		File[] contentList = directory.listFiles();
+
+		for (File elem : contentList) {
+			if (elem.isDirectory()) directoryNames.add(elem.getName());
+		}
+		
+		return directoryNames;
+	}
+	
 	/**
 	 * Executes given XPath expression for elements with JDOM Document as source.
 	 * 
@@ -79,6 +98,23 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for elements with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param xPath			XPath expression
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> xQueryElements(Document doc, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Element> expr = xFactory.compile(xPath, Filters.element(), null, ns);
+		List<Element> result = expr.evaluate(doc);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for elements with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -95,6 +131,24 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for elements with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param xPath			XPath expression
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> xQueryElements(Element elem, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Element> expr = xFactory.compile(xPath, Filters.element(), null, ns);
+		List<Element> result = expr.evaluate(elem);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for attributes with JDOM Document as source.
 	 * 
@@ -110,6 +164,23 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for attributes with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param xPath			XPath expression
+	 * @return List with all attributes found or empty list if no match found
+	 */
+	public static List<Attribute> xQueryAttributes(Document doc, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Attribute> expr = xFactory.compile(xPath, Filters.attribute(), null, ns);
+		List<Attribute> result = expr.evaluate(doc);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for attributes with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -126,6 +197,24 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for attributes with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param xPath			XPath expression
+	 * @return List with all attributes found or empty list if no match found
+	 */
+	public static List<Attribute> xQueryAttributes(Element elem, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Attribute> expr = xFactory.compile(xPath, Filters.attribute(), null, ns);
+		List<Attribute> result = expr.evaluate(elem);
+		
+		return result;
+	}
+	
 	/**
 	 * Searches a document for the first occurrence of an XML tag with JDOM Document as source.
 	 * 
@@ -141,6 +230,23 @@ public class XMLHelper {
 		return query.get(0);
 	}
 	
+	/**
+	 * Searches a document for the first occurrence of an XML tag with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param elementName	Name of the XML tag
+	 * @return First element found or NULL if no match found
+	 */
+	public static Element searchFirstElement(Document doc, String elementName, Namespace ns) {
+		//XPath index starts with 1; [] has a higher precedence than //
+		List<Element> query= xQueryElements(doc, "(//"+elementName+")[1]", ns);
+		
+		if(query.isEmpty()) return null;
+		return query.get(0);
+	}
+	
 	/**
 	 * Searches a document for the first occurrence of an XML tag with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -157,6 +263,24 @@ public class XMLHelper {
 		return query.get(0);
 	}
 	
+	/**
+	 * Searches a document for the first occurrence of an XML tag with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param elementName	Name of the XML tag
+	 * @return First element found or NULL if no match found
+	 */
+	public static Element searchFirstElement(Element elem, String elementName, Namespace ns) {
+		//XPath index starts with 1; [] has a higher precedence than //
+		List<Element> query= xQueryElements(elem, "(//"+elementName+")[1]", ns);
+		
+		if(query.isEmpty()) return null;
+		return query.get(0);
+	}
+	
 	/**
 	 * Searches a document for all occurrences of an XML tag with JDOM Document as source.
 	 * 
@@ -168,6 +292,19 @@ public class XMLHelper {
 		return xQueryElements(doc, "//"+elementName);
 	}
 	
+	/**
+	 * Searches a document for all occurrences of an XML tag with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param elementName	Name of the XML tag
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> searchAllElements(Document doc, String elementName, Namespace ns) {
+		return xQueryElements(doc, "//"+elementName, ns);
+	}
+	
 	/**
 	 * Searches a document for all occurrences of an XML tag with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -180,6 +317,20 @@ public class XMLHelper {
 		return xQueryElements(elem, "//"+elementName);
 	}
 	
+	/**
+	 * Searches a document for all occurrences of an XML tag with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param elementName	Name of the XML tag
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> searchAllElements(Element elem, String elementName, Namespace ns) {
+		return xQueryElements(elem, "//"+elementName, ns);
+	}
+	
 	/**
 	 * Checks whether two elements have an equal XML tag.
 	 * (Name of the XML tag and all attributes including their order must be equal)
diff --git a/src/var/FeatureModelImplGenerator/modelsetup.config b/src/var/FeatureModelImplGenerator/modelsetup.config
index c9861ed..b6d7141 100644
--- a/src/var/FeatureModelImplGenerator/modelsetup.config
+++ b/src/var/FeatureModelImplGenerator/modelsetup.config
@@ -10,7 +10,7 @@
 		<sensorconfig type="directory" path="./../FeatureModelConfigurator/resources/input/sensorconfig/" />
 		<!-- Path to all available .gazebo.xacro files. -->
 		<!-- Files with other endings are allowed and will be ignored. -->
-		<gazebomodel type="directory" path="./../../sim/turtlebot3/turtlebot3/turtlebot3_description/urdf/" />
+		<bot_description type="directory" path="./../../sim/turtlebot3/turtlebot3/turtlebot3_description/urdf/" />
 			<!-- <gazebomodel type="directory" path="./../FeatureModelConfigurator/resources/input/bot_description/" /> -->
 	</input>
 	<output>
diff --git a/src/var/FeatureModelImplGenerator/src/Adapter.java b/src/var/FeatureModelImplGenerator/src/Adapter.java
index e4e3580..106cffe 100644
--- a/src/var/FeatureModelImplGenerator/src/Adapter.java
+++ b/src/var/FeatureModelImplGenerator/src/Adapter.java
@@ -6,8 +6,8 @@ import org.jdom2.*;
 
 
 /**
- * Takes a featuremodel.xml file generated using FeatureModelConfigurator and a given ROS/ Gazebo .gazebo.xacro file.
- * The .gazebo.xacro file is modified to support the features specified in the featuremodel.xml file.
+ * Takes a featuremodel.xml file generated using FeatureModelConfigurator and a given .gazebo.xacro and urdf.xacro file.
+ * The .gazebo.xacro and .urdf.xacro files are modified to support the features specified in the featuremodel.xml file.
  */
 public class Adapter extends XMLHelper {
 	
@@ -32,29 +32,33 @@ public class Adapter extends XMLHelper {
 	/**
 	 * Call this to run Adapter.
 	 */
+	//public static void main(String[] args) {
 	public Adapter() {
 		System.out.println("Adapter started.");
 		
 		try {
 			final String SC_ENDING = ".xml";
-			final String GM_ENDING = ".gazebo.xacro";
+			final String GX_ENDING = ".gazebo.xacro";
+			final String UX_ENDING = ".urdf.xacro";
 			
 			//######### Input ###############################			
 			Document adapterConfig = readXml("./adapter.config");
 			
 			String in_fmPath = getConfigVal(adapterConfig, "input/featuremodel", "path");
 			String in_scPath = getConfigVal(adapterConfig, "input/sensorconfig", "path");
-			String in_gmPath = getConfigVal(adapterConfig, "input/gazebomodel", "path");
-			String out_gmPath = getConfigVal(adapterConfig, "output/gazebomodel", "path");
-			String out_bnPath = getConfigVal(adapterConfig, "output/botname", "path");
+			String in_descPath = getConfigVal(adapterConfig, "input/bot_description", "path");
+			String out_descPath = getConfigVal(adapterConfig, "output/bot_description", "path");
 			String in_fmName = getConfigVal(adapterConfig, "input/featuremodel", "name");
-			String in_gmName = "";
-			String out_gmName = getConfigVal(adapterConfig, "output/gazebomodel", "name");
-			String out_bnName = getConfigVal(adapterConfig, "output/botname", "name");
+			String in_gxName = "";
+			String in_uxName = "";
+			String out_gxName = getConfigVal(adapterConfig, "output/bot_description", "gazebo_xacro");
+			String out_uxName = getConfigVal(adapterConfig, "output/bot_description", "urdf_xacro");
+			String out_bnName = getConfigVal(adapterConfig, "output/bot_description", "botname");
 			
 			Document featureModel = readXml(in_fmPath+in_fmName);
 			List<Document> sensorConfigList = new ArrayList<Document>();
 			Document gazeboModel = null;
+			Document urdfModel = null;
 			
 			//Get the selected mode (either NO_CHANGE or Custom) as String.
 			String mode = "";
@@ -106,19 +110,34 @@ public class Adapter extends XMLHelper {
 				System.exit(-1);
 			}
 
-			in_gmName = botName+GM_ENDING;
+			in_gxName = botName+GX_ENDING;
 			
-			List<String> gmList = getFileNames(in_gmPath);
+			List<String> gxList = getFileNames(in_descPath);
 			boolean inDirectory = false;
-			for(String fileName : gmList) {
-				if(fileName.equals(in_gmName)) inDirectory = true;
+			for(String fileName : gxList) {
+				if(fileName.equals(in_gxName)) inDirectory = true;
 			}
 			if(!inDirectory) {
-				System.err.println("Cannot handle \'"+botName+"\': No input file known.");
+				System.err.println("Cannot handle \'"+botName+"\': No \'"+GX_ENDING+"\' input file known.");
 				System.exit(-1);
 			}
 			
-			gazeboModel = readXml(in_gmPath+in_gmName);
+			gazeboModel = readXml(in_descPath+in_gxName);
+			
+			//Get the corresponding .urdf.xacro file as Document.
+			in_uxName = botName+UX_ENDING;
+			
+			List<String> urdfList = getFileNames(in_descPath);
+			inDirectory = false;
+			for(String fileName : urdfList) {
+				if(fileName.equals(in_uxName)) inDirectory = true;
+			}
+			if(!inDirectory) {
+				System.err.println("Cannot handle \'"+botName+"\': No \'"+UX_ENDING+"\' input file known.");
+				System.exit(-1);
+			}
+			
+			urdfModel = readXml(in_descPath+in_uxName);
 			//###############################################
 			
 			
@@ -136,7 +155,7 @@ public class Adapter extends XMLHelper {
 							entry.detach();
 						}
 						else {
-							System.out.println("Found a <sensor> element in \'"+in_gmName+
+							System.out.println("Found a <sensor> element in \'"+in_gxName+
 								"\' that does not have a <gazebo> element as immediate parent. It was not removed, as this input is unexpected.");	
 						}
 					}
@@ -173,10 +192,41 @@ public class Adapter extends XMLHelper {
 			//###############################################
 			
 			
+			//######### Operate on .urdf.xacro ############
+			final Namespace ns = urdfModel.getRootElement().getNamespace("xacro");
+			
+			//Set <xacro:include> tags
+			List<Element> includeList = xQueryElements(urdfModel, "//xacro:include", ns);
+			if(includeList.size()!=2) {
+				System.err.println("Invalid input in \'"+in_uxName+"\': Expected exactly 2 xacro:include tags.");
+				System.exit(-1);
+			}
+			String destDirectoryName = (new File(out_descPath)).getName();
+			//includeList.get(0).setAttribute("filename", "$(find "+destDirectoryName+")/common_properties.xacro");//TODO
+			includeList.get(1).setAttribute("filename", "$(find "+destDirectoryName+")/"+out_gxName);
+			
+			switch(mode) {
+				case "NO_CHANGE":
+					break;
+				case "Custom":
+					//TODO
+					//If the urdf.xacro would be modified (to have an accurate visual model with the sensors attached to it), 
+					//this needs to happen here. (Delete or Insert <link>/ <joint> tags.)
+					//This would require separate files with <link>/ <joint> tags for each bot for the Adapter to access...
+					break;
+				default:
+					System.err.println("Invalid input in \'"+in_fmName+"\': NO_CHANGE/ Custom parameter could not be parsed.");
+					System.exit(-1);
+			}
+			//###############################################
+			
+			
 			//######### Output result #######################
-			saveAsXml(gazeboModel, out_gmPath+out_gmName);
+			saveAsXml(gazeboModel, out_descPath+out_gxName);
+			
+			saveAsXml(urdfModel, out_descPath+out_uxName);
 			
-			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(out_bnPath+out_bnName));
+			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(out_descPath+out_bnName));
 			bufferedWriter.write(botName);
 			bufferedWriter.close();
 			//###############################################
diff --git a/src/var/FeatureModelImplGenerator/src/ModelSetup.java b/src/var/FeatureModelImplGenerator/src/ModelSetup.java
index dacfab2..2ed7624 100644
--- a/src/var/FeatureModelImplGenerator/src/ModelSetup.java
+++ b/src/var/FeatureModelImplGenerator/src/ModelSetup.java
@@ -7,7 +7,7 @@ import org.jdom2.*;
 
 /**
  * Takes the specified template for a Feature IDE model file and adds all available
- * sensorconfig/ gazebo model files.
+ * sensorconfig/ gazebo.xacro model files.
  */
 public class ModelSetup extends XMLHelper{
 
@@ -37,14 +37,14 @@ public class ModelSetup extends XMLHelper{
 		
 		try {
 			final String SC_ENDING = ".xml";
-			final String GM_ENDING = ".gazebo.xacro";
+			final String GX_ENDING = ".gazebo.xacro";
 			
 			//######### Input ###############################			
 			Document ModelSetupConfig = readXml("./modelsetup.config");
 			
 			String in_mPath = getConfigVal(ModelSetupConfig, "input/model", "path");
 			String in_scPath = getConfigVal(ModelSetupConfig, "input/sensorconfig", "path");
-			String in_gmPath = getConfigVal(ModelSetupConfig, "input/gazebomodel", "path");
+			String in_descPath = getConfigVal(ModelSetupConfig, "input/bot_description", "path");
 			String out_mPath = getConfigVal(ModelSetupConfig, "output/model", "path");
 			String in_mName = getConfigVal(ModelSetupConfig, "input/model", "name");
 			String out_mName = getConfigVal(ModelSetupConfig, "output/model", "name");
@@ -52,14 +52,14 @@ public class ModelSetup extends XMLHelper{
 			Document model = readXml(in_mPath+in_mName);
 			
 			//Get sensorconfig- and gazebomodel file names.
-			//All files in in_scPath will be treated as sensorconfigs, while in_gmPath may
+			//All files in in_scPath will be treated as sensorconfigs, while in_descPath may
 			//contain other files that will be filtered out!
 			List<String> scList = getFileNames(in_scPath);
-			List<String> tmpList = getFileNames(in_gmPath);
-			List<String> gmList = new ArrayList<String>();
+			List<String> tmpList = getFileNames(in_descPath);
+			List<String> gxList = new ArrayList<String>();
 			for(String s : tmpList) {
-				if(s.endsWith(GM_ENDING)) {
-					gmList.add(s);
+				if(s.endsWith(GX_ENDING)) {
+					gxList.add(s);
 				}
 			}
 			
@@ -69,8 +69,8 @@ public class ModelSetup extends XMLHelper{
 			if(scList.isEmpty()) {
 				System.out.println("Warning: No sensorconfig files found in \'"+in_scPath+"\'.");
 			}
-			if(gmList.isEmpty()) {
-				System.err.println("Error: No gazebo model files found in \'"+in_gmPath+"\'.");
+			if(gxList.isEmpty()) {
+				System.err.println("Error: No \'"+GX_ENDING+"\' files found in \'"+in_descPath+"\'.");
 				System.exit(-1);
 			}
 			
@@ -84,7 +84,7 @@ public class ModelSetup extends XMLHelper{
 			
 			//Sort features alphabetically (ascending order; upper case first; then lower case).
 			Collections.sort(scList);
-			Collections.sort(gmList);
+			Collections.sort(gxList);
 			//###############################################
 			
 			
@@ -117,8 +117,8 @@ public class ModelSetup extends XMLHelper{
 				custom.addContent(feature);
 			}
 			
-			for(String gm : gmList) {
-				String featureName = gm.substring(0, gm.length() - GM_ENDING.length());
+			for(String gx : gxList) {
+				String featureName = gx.substring(0, gx.length() - GX_ENDING.length());
 				Element feature = new Element("feature").setAttribute("name", featureName);
 				templates.addContent(feature);
 			}
diff --git a/src/var/FeatureModelImplGenerator/src/XMLHelper.java b/src/var/FeatureModelImplGenerator/src/XMLHelper.java
index 30d0f3b..3a1030b 100644
--- a/src/var/FeatureModelImplGenerator/src/XMLHelper.java
+++ b/src/var/FeatureModelImplGenerator/src/XMLHelper.java
@@ -57,13 +57,32 @@ public class XMLHelper {
 		File directory = new File(path);
 		File[] contentList = directory.listFiles();
 
-		for (File file : contentList) {
-			if (file.isFile()) fileNames.add(file.getName());
+		for (File elem : contentList) {
+			if (elem.isFile()) fileNames.add(elem.getName());
 		}
 		
 		return fileNames;
 	}
 	
+	/**
+	 * Takes a given directory path and lists all directories directly in it.
+	 * (Does not visit sub-directories recursively)
+	 * 
+	 * @param path			Path to the directory
+	 * @return ArrayList with all directory names
+	 */
+	public static ArrayList<String> getDirectoryNames(String path) {
+		ArrayList<String> directoryNames = new ArrayList<String>();
+		File directory = new File(path);
+		File[] contentList = directory.listFiles();
+
+		for (File elem : contentList) {
+			if (elem.isDirectory()) directoryNames.add(elem.getName());
+		}
+		
+		return directoryNames;
+	}
+	
 	/**
 	 * Executes given XPath expression for elements with JDOM Document as source.
 	 * 
@@ -79,6 +98,23 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for elements with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param xPath			XPath expression
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> xQueryElements(Document doc, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Element> expr = xFactory.compile(xPath, Filters.element(), null, ns);
+		List<Element> result = expr.evaluate(doc);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for elements with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -95,6 +131,24 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for elements with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param xPath			XPath expression
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> xQueryElements(Element elem, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Element> expr = xFactory.compile(xPath, Filters.element(), null, ns);
+		List<Element> result = expr.evaluate(elem);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for attributes with JDOM Document as source.
 	 * 
@@ -110,6 +164,23 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for attributes with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param xPath			XPath expression
+	 * @return List with all attributes found or empty list if no match found
+	 */
+	public static List<Attribute> xQueryAttributes(Document doc, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Attribute> expr = xFactory.compile(xPath, Filters.attribute(), null, ns);
+		List<Attribute> result = expr.evaluate(doc);
+		
+		return result;
+	}
+	
 	/**
 	 * Executes given XPath expression for attributes with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -126,6 +197,24 @@ public class XMLHelper {
 		return result;
 	}
 	
+	/**
+	 * Executes given XPath expression for attributes with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param xPath			XPath expression
+	 * @return List with all attributes found or empty list if no match found
+	 */
+	public static List<Attribute> xQueryAttributes(Element elem, String xPath, Namespace ns) {
+		XPathFactory xFactory = XPathFactory.instance();
+		XPathExpression<Attribute> expr = xFactory.compile(xPath, Filters.attribute(), null, ns);
+		List<Attribute> result = expr.evaluate(elem);
+		
+		return result;
+	}
+	
 	/**
 	 * Searches a document for the first occurrence of an XML tag with JDOM Document as source.
 	 * 
@@ -141,6 +230,23 @@ public class XMLHelper {
 		return query.get(0);
 	}
 	
+	/**
+	 * Searches a document for the first occurrence of an XML tag with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param elementName	Name of the XML tag
+	 * @return First element found or NULL if no match found
+	 */
+	public static Element searchFirstElement(Document doc, String elementName, Namespace ns) {
+		//XPath index starts with 1; [] has a higher precedence than //
+		List<Element> query= xQueryElements(doc, "(//"+elementName+")[1]", ns);
+		
+		if(query.isEmpty()) return null;
+		return query.get(0);
+	}
+	
 	/**
 	 * Searches a document for the first occurrence of an XML tag with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -157,6 +263,24 @@ public class XMLHelper {
 		return query.get(0);
 	}
 	
+	/**
+	 * Searches a document for the first occurrence of an XML tag with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param elementName	Name of the XML tag
+	 * @return First element found or NULL if no match found
+	 */
+	public static Element searchFirstElement(Element elem, String elementName, Namespace ns) {
+		//XPath index starts with 1; [] has a higher precedence than //
+		List<Element> query= xQueryElements(elem, "(//"+elementName+")[1]", ns);
+		
+		if(query.isEmpty()) return null;
+		return query.get(0);
+	}
+	
 	/**
 	 * Searches a document for all occurrences of an XML tag with JDOM Document as source.
 	 * 
@@ -168,6 +292,19 @@ public class XMLHelper {
 		return xQueryElements(doc, "//"+elementName);
 	}
 	
+	/**
+	 * Searches a document for all occurrences of an XML tag with JDOM Document as source.
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param doc			The JDOM Document
+	 * @param elementName	Name of the XML tag
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> searchAllElements(Document doc, String elementName, Namespace ns) {
+		return xQueryElements(doc, "//"+elementName, ns);
+	}
+	
 	/**
 	 * Searches a document for all occurrences of an XML tag with Element as source.
 	 * (Expression is executed on the entire Document the Element is attached to!)
@@ -180,6 +317,20 @@ public class XMLHelper {
 		return xQueryElements(elem, "//"+elementName);
 	}
 	
+	/**
+	 * Searches a document for all occurrences of an XML tag with Element as source.
+	 * (Expression is executed on the entire Document the Element is attached to!)
+	 * (Can handle namespaces.)
+	 * 
+	 * @param ns			The XML Namespace
+	 * @param elem			The Element
+	 * @param elementName	Name of the XML tag
+	 * @return List with all elements found or empty list if no match found
+	 */
+	public static List<Element> searchAllElements(Element elem, String elementName, Namespace ns) {
+		return xQueryElements(elem, "//"+elementName, ns);
+	}
+	
 	/**
 	 * Checks whether two elements have an equal XML tag.
 	 * (Name of the XML tag and all attributes including their order must be equal)
-- 
GitLab