]> git.basschouten.com Git - openhab-addons.git/commitdiff
[dwdunwetter] Rework channel creation (#9229)
authorChristoph Weitkamp <github@christophweitkamp.de>
Tue, 8 Dec 2020 04:53:29 +0000 (05:53 +0100)
committerGitHub <noreply@github.com>
Tue, 8 Dec 2020 04:53:29 +0000 (20:53 -0800)
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
bundles/org.openhab.binding.dwdunwetter/src/main/java/org/openhab/binding/dwdunwetter/internal/config/DwdUnwetterConfiguration.java
bundles/org.openhab.binding.dwdunwetter/src/main/java/org/openhab/binding/dwdunwetter/internal/handler/DwdUnwetterHandler.java
bundles/org.openhab.binding.dwdunwetter/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.dwdunwetter/src/test/java/org/openhab/binding/dwdunwetter/DwdUnwetterHandlerTest.java [deleted file]
bundles/org.openhab.binding.dwdunwetter/src/test/java/org/openhab/binding/dwdunwetter/internal/handler/DwdUnwetterHandlerTest.java [new file with mode: 0644]

index 83535537c5f627b2c6070ecd555062a861feeb2f..a7bbde15646fccdfedcab07011c77159a1f051ec 100644 (file)
@@ -22,6 +22,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 @NonNullByDefault
 public class DwdUnwetterConfiguration {
     public int refresh;
-    public int warningCount;
+    public int warningCount = 1;
     public String cellId = "";
 }
index 7ae12b2f783d9560cd251a179fe933417f666055..d271fc8c38ef717311381a84bf2443a1d4d97cb1 100644 (file)
@@ -18,6 +18,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
@@ -30,9 +31,10 @@ import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingStatus;
 import org.openhab.core.thing.binding.BaseThingHandler;
-import org.openhab.core.thing.binding.builder.ChannelBuilder;
-import org.openhab.core.thing.type.ChannelKind;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
 import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.thing.util.ThingHandlerHelper;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.RefreshType;
 import org.openhab.core.types.State;
@@ -54,7 +56,6 @@ public class DwdUnwetterHandler extends BaseThingHandler {
     private @Nullable DwdWarningsData data;
 
     private boolean inRefresh;
-    private boolean initializing;
 
     public DwdUnwetterHandler(Thing thing) {
         super(thing);
@@ -79,14 +80,8 @@ public class DwdUnwetterHandler extends BaseThingHandler {
             return;
         }
 
-        if (initializing) {
-            logger.trace("Still initializing. Ignoring refresh request.");
-            return;
-        }
-
-        ThingStatus status = getThing().getStatus();
-        if (status != ThingStatus.ONLINE && status != ThingStatus.UNKNOWN) {
-            logger.debug("Unable to refresh. Thing status is {}", status);
+        if (!ThingHandlerHelper.isHandlerInitialized(getThing())) {
+            logger.debug("Unable to refresh. Thing status is '{}'", getThing().getStatus());
             return;
         }
 
@@ -105,9 +100,7 @@ public class DwdUnwetterHandler extends BaseThingHandler {
             return;
         }
 
-        if (status == ThingStatus.UNKNOWN) {
-            updateStatus(ThingStatus.ONLINE);
-        }
+        updateStatus(ThingStatus.ONLINE);
 
         updateState(getChannelUuid(CHANNEL_LAST_UPDATED), new DateTimeType());
 
@@ -142,18 +135,36 @@ public class DwdUnwetterHandler extends BaseThingHandler {
     @Override
     public void initialize() {
         logger.debug("Start initializing!");
-        initializing = true;
         updateStatus(ThingStatus.UNKNOWN);
 
         DwdUnwetterConfiguration config = getConfigAs(DwdUnwetterConfiguration.class);
-        warningCount = config.warningCount;
+        int newWarningCount = config.warningCount;
+
+        if (warningCount != newWarningCount) {
+            List<Channel> toBeAddedChannels = new ArrayList<>();
+            List<Channel> toBeRemovedChannels = new ArrayList<>();
+            if (warningCount > newWarningCount) {
+                for (int i = newWarningCount + 1; i <= warningCount; ++i) {
+                    toBeRemovedChannels.addAll(removeChannels(i));
+                }
+            } else {
+                for (int i = warningCount + 1; i <= newWarningCount; ++i) {
+                    toBeAddedChannels.addAll(createChannels(i));
+                }
+            }
+            warningCount = newWarningCount;
 
-        data = new DwdWarningsData(config.cellId);
+            ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
+            for (Channel channel : toBeAddedChannels) {
+                builder.withChannel(channel);
+            }
+            updateThing(builder.build());
+        }
 
-        updateThing(editThing().withChannels(createChannels()).build());
+        data = new DwdWarningsData(config.cellId);
 
         refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refresh, TimeUnit.MINUTES);
-        initializing = false;
+
         logger.debug("Finished initializing!");
     }
 
@@ -166,69 +177,68 @@ public class DwdUnwetterHandler extends BaseThingHandler {
     }
 
     /**
-     * Creates a trigger Channel.
+     * Creates a normal, state based, channel associated with a warning.
      */
-    private Channel createTriggerChannel(String typeId, String label, int warningNumber) {
+    private void createChannelIfNotExist(ThingHandlerCallback cb, List<Channel> channels, String typeId, String label,
+            int warningNumber) {
         ChannelUID channelUID = getChannelUuid(typeId, warningNumber);
-        return ChannelBuilder.create(channelUID, "String") //
-                .withType(new ChannelTypeUID(BINDING_ID, typeId)) //
-                .withLabel(label + " (" + (warningNumber + 1) + ")")//
-                .withKind(ChannelKind.TRIGGER) //
-                .build();
+        Channel existingChannel = getThing().getChannel(channelUID);
+        if (existingChannel != null) {
+            logger.trace("Thing '{}' already has an existing channel '{}'. Omit adding new channel '{}'.",
+                    getThing().getUID(), existingChannel.getUID(), channelUID);
+        } else {
+            channels.add(cb.createChannelBuilder(channelUID, new ChannelTypeUID(BINDING_ID, typeId))
+                    .withLabel(label + " " + getChannelLabelSuffix(warningNumber)).build());
+        }
     }
 
-    /**
-     * Creates a normal, state based, channel associated with a warning.
-     */
-    private Channel createChannel(String typeId, String itemType, String label, int warningNumber) {
-        ChannelUID channelUID = getChannelUuid(typeId, warningNumber);
-        return ChannelBuilder.create(channelUID, itemType) //
-                .withType(new ChannelTypeUID(BINDING_ID, typeId)) //
-                .withLabel(label + " (" + (warningNumber + 1) + ")")//
-                .build();
+    private String getChannelLabelSuffix(int warningNumber) {
+        return "(" + (warningNumber + 1) + ")";
     }
 
     /**
-     * Creates a normal, state based, channel not associated with a warning.
+     * Creates the Channels for each warning.
+     *
+     * @return The List of Channels to be added
      */
-    private Channel createChannel(String typeId, String itemType, String label) {
-        ChannelUID channelUID = getChannelUuid(typeId);
-        return ChannelBuilder.create(channelUID, itemType) //
-                .withType(new ChannelTypeUID(BINDING_ID, typeId)) //
-                .withLabel(label)//
-                .build();
+    private List<Channel> createChannels(int warningNumber) {
+        logger.debug("Building channels for thing '{}'.", getThing().getUID());
+        List<Channel> channels = new ArrayList<>();
+        ThingHandlerCallback callback = getCallback();
+        if (callback != null) {
+            createChannelIfNotExist(callback, channels, CHANNEL_UPDATED, "Updated", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_WARNING, "Warning", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_SEVERITY, "Severity", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_DESCRIPTION, "Description", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_EFFECTIVE, "Issued", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_ONSET, "Valid From", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_EXPIRES, "Valid To", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_EVENT, "Type", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_HEADLINE, "Headline", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_ALTITUDE, "Height (from)", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_CEILING, "Height (to)", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_INSTRUCTION, "Instruction", warningNumber);
+            createChannelIfNotExist(callback, channels, CHANNEL_URGENCY, "Urgency", warningNumber);
+        }
+        return channels;
     }
 
     /**
-     * Creates the ChannelsT for each warning.
+     * Filters the Channels for each warning
      *
-     * @return The List of Channels
+     * @return The List of Channels to be removed
      */
-    private List<Channel> createChannels() {
-        List<Channel> channels = new ArrayList<>(warningCount * 11 + 1);
-        channels.add(createChannel(CHANNEL_LAST_UPDATED, "DateTime", "Last Updated"));
-        for (int i = 0; i < warningCount; i++) {
-            channels.add(createChannel(CHANNEL_WARNING, "Switch", "Warning", i));
-            channels.add(createTriggerChannel(CHANNEL_UPDATED, "Updated", i));
-            channels.add(createChannel(CHANNEL_SEVERITY, "String", "Severity", i));
-            channels.add(createChannel(CHANNEL_DESCRIPTION, "String", "Description", i));
-            channels.add(createChannel(CHANNEL_EFFECTIVE, "DateTime", "Issued", i));
-            channels.add(createChannel(CHANNEL_ONSET, "DateTime", "Valid From", i));
-            channels.add(createChannel(CHANNEL_EXPIRES, "DateTime", "Valid To", i));
-            channels.add(createChannel(CHANNEL_EVENT, "String", "Type", i));
-            channels.add(createChannel(CHANNEL_HEADLINE, "String", "Headline", i));
-            channels.add(createChannel(CHANNEL_ALTITUDE, "Number:Length", "Height (from)", i));
-            channels.add(createChannel(CHANNEL_CEILING, "Number:Length", "Height (to)", i));
-            channels.add(createChannel(CHANNEL_INSTRUCTION, "String", "Instruction", i));
-            channels.add(createChannel(CHANNEL_URGENCY, "String", "Urgency", i));
-        }
-        return channels;
+    @SuppressWarnings("null")
+    private List<Channel> removeChannels(int warningNumber) {
+        return getThing().getChannels().stream()
+                .filter(channel -> channel.getLabel() != null
+                        && channel.getLabel().endsWith(getChannelLabelSuffix(warningNumber)))
+                .collect(Collectors.toList());
     }
 
     @Override
     public void dispose() {
         final ScheduledFuture<?> job = refreshJob;
-
         if (job != null) {
             job.cancel(true);
         }
index 714e92133f61fc38fc70c5b1a12a33657d9f56f0..5a25f8beef9da4375998a7324bda5e8a0d189077 100644 (file)
@@ -7,6 +7,9 @@
        <thing-type id="dwdwarnings">
                <label>Weather Warnings</label>
                <description>Weather Warnings for an area</description>
+               <channels>
+                       <channel typeId="lastUpdated" id="lastUpdated"></channel>
+               </channels>
                <config-description>
                        <parameter name="cellId" type="text" required="true">
                                <label>Cell-ID</label>
diff --git a/bundles/org.openhab.binding.dwdunwetter/src/test/java/org/openhab/binding/dwdunwetter/DwdUnwetterHandlerTest.java b/bundles/org.openhab.binding.dwdunwetter/src/test/java/org/openhab/binding/dwdunwetter/DwdUnwetterHandlerTest.java
deleted file mode 100644 (file)
index 7673057..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/**
- * Copyright (c) 2010-2020 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.dwdunwetter;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.*;
-
-import java.io.InputStream;
-import java.util.List;
-import java.util.Objects;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import org.hamcrest.CoreMatchers;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.quality.Strictness;
-import org.openhab.binding.dwdunwetter.internal.DwdUnwetterBindingConstants;
-import org.openhab.binding.dwdunwetter.internal.handler.DwdUnwetterHandler;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.test.java.JavaTest;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusInfo;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.thing.type.ChannelTypeUID;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-/**
- * Test cases for {@link DwdUnwetterHandler}. The tests provide mocks for supporting entities using Mockito.
- *
- * @author Martin Koehler - Initial contribution
- */
-@ExtendWith(MockitoExtension.class)
-@MockitoSettings(strictness = Strictness.WARN)
-public class DwdUnwetterHandlerTest extends JavaTest {
-
-    private ThingHandler handler;
-
-    private @Mock ThingHandlerCallback callback;
-    private @Mock Thing thing;
-
-    @BeforeEach
-    public void setUp() {
-        handler = new DwdUnwetterHandler(thing);
-        handler.setCallback(callback);
-        // mock getConfiguration to prevent NPEs
-        when(thing.getUID()).thenReturn(new ThingUID(DwdUnwetterBindingConstants.BINDING_ID, "test"));
-        Configuration configuration = new Configuration();
-        configuration.put("refresh", Integer.valueOf("1"));
-        configuration.put("warningCount", Integer.valueOf("1"));
-        when(thing.getConfiguration()).thenReturn(configuration);
-    }
-
-    @Test
-    public void testInitializeShouldCallTheCallback() {
-        // we expect the handler#initialize method to call the callback during execution and
-        // pass it the thing and a ThingStatusInfo object containing the ThingStatus of the thing.
-        handler.initialize();
-
-        // the argument captor will capture the argument of type ThingStatusInfo given to the
-        // callback#statusUpdated method.
-        ArgumentCaptor<ThingStatusInfo> statusInfoCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
-
-        // verify the interaction with the callback and capture the ThingStatusInfo argument:
-        waitForAssert(() -> {
-            verify(callback, times(1)).statusUpdated(eq(thing), statusInfoCaptor.capture());
-        });
-
-        // assert that the (temporary) UNKNOWN status was to the mocked thing first:
-        assertThat(statusInfoCaptor.getAllValues().get(0).getStatus(), is(ThingStatus.UNKNOWN));
-    }
-
-    /**
-     * Tests that the labels of the channels are equal to the ChannelType Definition
-     */
-    @Test
-    public void testLabels() throws Exception {
-        handler.initialize();
-
-        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-        DocumentBuilder builder = factory.newDocumentBuilder();
-        InputStream stream = getClass().getResourceAsStream("/OH-INF/thing/thing-types.xml");
-        Document document = builder.parse(stream);
-        NodeList nodeList = document.getElementsByTagName("channel-type");
-
-        thing = handler.getThing();
-        List<Channel> channels = thing.getChannels();
-        for (Channel channel : channels) {
-            String label = getLabel(nodeList, channel.getChannelTypeUID());
-            assertThat(channel.getLabel(), CoreMatchers.startsWith(label));
-        }
-    }
-
-    private String getLabel(NodeList nodeList, ChannelTypeUID uuid) {
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            Node node = nodeList.item(i);
-            Node nodeId = node.getAttributes().getNamedItem("id");
-            if (nodeId == null) {
-                continue;
-            }
-            if (Objects.equals(nodeId.getTextContent(), uuid.getId())) {
-                return getLabel(node.getChildNodes());
-            }
-        }
-        return null;
-    }
-
-    private String getLabel(NodeList nodeList) {
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            Node node = nodeList.item(i);
-            if (node.getNodeName().equals("label")) {
-                return node.getTextContent();
-            }
-        }
-        return null;
-    }
-}
diff --git a/bundles/org.openhab.binding.dwdunwetter/src/test/java/org/openhab/binding/dwdunwetter/internal/handler/DwdUnwetterHandlerTest.java b/bundles/org.openhab.binding.dwdunwetter/src/test/java/org/openhab/binding/dwdunwetter/internal/handler/DwdUnwetterHandlerTest.java
new file mode 100644 (file)
index 0000000..76afca3
--- /dev/null
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dwdunwetter.internal.handler;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Objects;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.openhab.binding.dwdunwetter.internal.DwdUnwetterBindingConstants;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.test.java.JavaTest;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusInfo;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Test cases for {@link DwdUnwetterHandler}. The tests provide mocks for supporting entities using Mockito.
+ *
+ * @author Martin Koehler - Initial contribution
+ */
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.WARN)
+public class DwdUnwetterHandlerTest extends JavaTest {
+
+    private ThingHandler handler;
+
+    private @Mock ThingHandlerCallback callback;
+    private @Mock Thing thing;
+
+    @BeforeEach
+    public void setUp() {
+        when(callback.createChannelBuilder(any(ChannelUID.class), any(ChannelTypeUID.class)))
+                .thenAnswer(invocation -> ChannelBuilder.create(invocation.getArgument(0, ChannelUID.class))
+                        .withType(invocation.getArgument(1, ChannelTypeUID.class)));
+
+        handler = new DwdUnwetterHandler(thing);
+        handler.setCallback(callback);
+        // mock getConfiguration to prevent NPEs
+        when(thing.getUID()).thenReturn(new ThingUID(DwdUnwetterBindingConstants.BINDING_ID, "test"));
+        Configuration configuration = new Configuration();
+        configuration.put("refresh", Integer.valueOf("1"));
+        configuration.put("warningCount", Integer.valueOf("1"));
+        when(thing.getConfiguration()).thenReturn(configuration);
+    }
+
+    @Test
+    public void testInitializeShouldCallTheCallback() {
+        // we expect the handler#initialize method to call the callback during execution and
+        // pass it the thing and a ThingStatusInfo object containing the ThingStatus of the thing.
+        handler.initialize();
+
+        // the argument captor will capture the argument of type ThingStatusInfo given to the
+        // callback#statusUpdated method.
+        ArgumentCaptor<ThingStatusInfo> statusInfoCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
+
+        // verify the interaction with the callback and capture the ThingStatusInfo argument:
+        waitForAssert(() -> {
+            verify(callback, times(1)).statusUpdated(eq(thing), statusInfoCaptor.capture());
+        });
+
+        // assert that the (temporary) UNKNOWN status was to the mocked thing first:
+        assertThat(statusInfoCaptor.getAllValues().get(0).getStatus(), is(ThingStatus.UNKNOWN));
+    }
+
+    /**
+     * Tests that the labels of the channels are equal to the ChannelType Definition
+     */
+    @Test
+    public void testLabels() throws Exception {
+        handler.initialize();
+
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        InputStream stream = getClass().getResourceAsStream("/OH-INF/thing/thing-types.xml");
+        Document document = builder.parse(stream);
+        NodeList nodeList = document.getElementsByTagName("channel-type");
+
+        thing = handler.getThing();
+        List<Channel> channels = thing.getChannels();
+        for (Channel channel : channels) {
+            String label = getLabel(nodeList, channel.getChannelTypeUID());
+            assertThat(channel.getLabel(), CoreMatchers.startsWith(label));
+        }
+    }
+
+    private String getLabel(NodeList nodeList, ChannelTypeUID uuid) {
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            Node node = nodeList.item(i);
+            Node nodeId = node.getAttributes().getNamedItem("id");
+            if (nodeId == null) {
+                continue;
+            }
+            if (Objects.equals(nodeId.getTextContent(), uuid.getId())) {
+                return getLabel(node.getChildNodes());
+            }
+        }
+        return null;
+    }
+
+    private String getLabel(NodeList nodeList) {
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            Node node = nodeList.item(i);
+            if (node.getNodeName().equals("label")) {
+                return node.getTextContent();
+            }
+        }
+        return null;
+    }
+}