You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rave.apache.org by sc...@apache.org on 2012/03/21 12:41:54 UTC

svn commit: r1303364 - in /incubator/rave/trunk/rave-providers/rave-w3c-provider/src: main/java/org/apache/rave/provider/w3c/repository/ main/java/org/apache/rave/provider/w3c/repository/impl/ main/java/org/apache/rave/provider/w3c/service/impl/ test/j...

Author: scottbw
Date: Wed Mar 21 11:41:54 2012
New Revision: 1303364

URL: http://svn.apache.org/viewvc?rev=1303364&view=rev
Log:
Modified the W3C Provider to use the Wookie Connector to get widget metadata, rather than a separate parser. This means we're missing some metadata in the current (0.9.2) version of the connector, but should be good to update to 0.10

Modified:
    incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/W3CWidgetMetadataRepository.java
    incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/impl/WookieWidgetMetadataRepository.java
    incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolver.java
    incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetService.java
    incubator/rave/trunk/rave-providers/rave-w3c-provider/src/test/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolverTest.java

Modified: incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/W3CWidgetMetadataRepository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/W3CWidgetMetadataRepository.java?rev=1303364&r1=1303363&r2=1303364&view=diff
==============================================================================
--- incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/W3CWidgetMetadataRepository.java (original)
+++ incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/W3CWidgetMetadataRepository.java Wed Mar 21 11:41:54 2012
@@ -19,6 +19,8 @@
 
 package org.apache.rave.provider.w3c.repository;
 
+import org.apache.rave.portal.model.Widget;
+
 public interface W3CWidgetMetadataRepository {
     /**
      * Fetches widget metadata for the widget via
@@ -28,5 +30,11 @@ public interface W3CWidgetMetadataReposi
      * @param widgetGuid The widget to fetch metadata for.
      * @return The string response from the w3c widget server.
      */
-    public String getWidgetMetadata(String widgetGuid);
+    public Widget getWidgetMetadata(String widgetGuid);
+    
+    /**
+     * Fetches widget metadata for all available widgets
+     * @return an array of Widget objects representing available W3C widgets
+     */
+    public Widget[] getWidgetMetadata();
 }

Modified: incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/impl/WookieWidgetMetadataRepository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/impl/WookieWidgetMetadataRepository.java?rev=1303364&r1=1303363&r2=1303364&view=diff
==============================================================================
--- incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/impl/WookieWidgetMetadataRepository.java (original)
+++ incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/repository/impl/WookieWidgetMetadataRepository.java Wed Mar 21 11:41:54 2012
@@ -19,15 +19,16 @@
 
 package org.apache.rave.provider.w3c.repository.impl;
 
+import org.apache.rave.portal.model.Widget;
+import org.apache.rave.portal.service.WidgetProviderService;
 import org.apache.rave.provider.w3c.repository.W3CWidgetMetadataRepository;
+import org.apache.rave.provider.w3c.service.impl.WookieWidgetService;
+import org.apache.wookie.connector.framework.WookieConnectorException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Repository;
-import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestOperations;
 
 /**
  * Handles the call to wookies metadata service
@@ -36,15 +37,11 @@ import org.springframework.web.client.Re
 @Repository
 public class WookieWidgetMetadataRepository implements W3CWidgetMetadataRepository {
     private static Logger logger = LoggerFactory.getLogger(WookieWidgetMetadataRepository.class);
-    private RestOperations restOperations;
-    private String wookieUrl;
+    private WookieWidgetService widgetService;
 
     @Autowired
-    public WookieWidgetMetadataRepository(@Qualifier(value = "xmlStringCompatibleRestTemplate") RestOperations restOperations,
-                                          @Value("${provider.wookie.wookieServerUrl}") String wookieRoot) {
-        this.restOperations = restOperations;
-        this.wookieUrl = wookieRoot + "/widgets/";
-        logger.debug("Wookie render Url: {}", wookieUrl);
+    public WookieWidgetMetadataRepository(@Qualifier("wookieWidgetService") WidgetProviderService widgetService) {
+    	this.widgetService = (WookieWidgetService) widgetService;
     }
 
     /*
@@ -52,15 +49,26 @@ public class WookieWidgetMetadataReposit
      * @see org.apache.rave.provider.w3c.repository.W3CWidgetMetadataRepository#getWidgetMetadata(java.lang.String)
      */
     @Override
-    public String getWidgetMetadata(String widgetGuid) {
-        String responseString = null;
+    public Widget getWidgetMetadata(String widgetGuid) {
         try {
-            responseString = restOperations.getForObject(wookieUrl + widgetGuid, String.class);
-        } catch (RestClientException e) {
+        	return this.widgetService.getWidget(widgetGuid);
+        } catch (WookieConnectorException e) {
             throw new IllegalArgumentException("Error occurred while processing response from wookie metadata call", e);
         }
-        // return the raw xml
-        return responseString;
     }
 
+	/* (non-Javadoc)
+	 * @see org.apache.rave.provider.w3c.repository.W3CWidgetMetadataRepository#getWidgetMetadata()
+	 */
+	@Override
+	public Widget[] getWidgetMetadata() {
+		try {
+			return this.widgetService.getWidgets();
+		} catch (WookieConnectorException e) {
+            throw new IllegalArgumentException("Error occurred while processing response from wookie metadata call", e);
+		}
+	}
+    
+    
+
 }

Modified: incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolver.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolver.java?rev=1303364&r1=1303363&r2=1303364&view=diff
==============================================================================
--- incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolver.java (original)
+++ incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolver.java Wed Mar 21 11:41:54 2012
@@ -21,26 +21,15 @@ package org.apache.rave.provider.w3c.ser
 
 import org.apache.rave.portal.model.Widget;
 import org.apache.rave.portal.service.WidgetMetadataResolver;
+import org.apache.rave.portal.service.WidgetProviderService;
 import org.apache.rave.provider.w3c.Constants;
 import org.apache.rave.provider.w3c.repository.W3CWidgetMetadataRepository;
+import org.apache.rave.provider.w3c.repository.impl.WookieWidgetMetadataRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
 
 @Component
 public class WookieWidgetMetadataResolver implements WidgetMetadataResolver {
@@ -48,8 +37,8 @@ public class WookieWidgetMetadataResolve
     private W3CWidgetMetadataRepository widgetMetadataRepository;
 
     @Autowired
-    public WookieWidgetMetadataResolver(W3CWidgetMetadataRepository widgetMetadataRepository){
-        this.widgetMetadataRepository = widgetMetadataRepository;
+    public WookieWidgetMetadataResolver(@Qualifier("wookieWidgetService") WidgetProviderService widgetService){
+        this.widgetMetadataRepository = new WookieWidgetMetadataRepository(widgetService);
     }
 
     /*
@@ -66,9 +55,7 @@ public class WookieWidgetMetadataResolve
      */
     public Widget getMetadata(String url) {
         try {
-            String xmlResult = widgetMetadataRepository.getWidgetMetadata(url);
-            Widget[] widgets = processResponse(xmlResult);
-            return widgets[0];
+            return widgetMetadataRepository.getWidgetMetadata(url);
         } catch (Exception e) {
             throw new IllegalArgumentException("Error occurred while processing response for Widget metadata call", e);
         }
@@ -81,84 +68,10 @@ public class WookieWidgetMetadataResolve
     @Override
     public Widget[] getMetadataGroup(String url) {
         try {
-            String xmlResult = widgetMetadataRepository.getWidgetMetadata(url);
-            return processResponse(xmlResult);
+            return widgetMetadataRepository.getWidgetMetadata();
         } catch (Exception e) {
             throw new IllegalArgumentException("Error occurred while processing response for Widget (group) metadata call", e);
         }
     }
 
-    /**
-     * Method sets up the xml parsing routine. (from a string supplied from wookie)
-     * @param rawXml - raw xml string
-     * @return an array of widgets
-     */
-    private Widget[] processResponse(String rawXml){
-        Widget[] widgets = null;
-        try {
-            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-            DocumentBuilder db = dbf.newDocumentBuilder();
-            Document xml = db.parse(new InputSource(new StringReader(rawXml)));
-            widgets = parseWookieAdvert(xml);
-        } catch (ParserConfigurationException e) {
-            throw new IllegalArgumentException("Error occurred while processing response for Widget metadata call", e);
-        } catch (SAXException e) {
-            throw new IllegalArgumentException("Error occurred while processing response for Widget metadata call", e);
-        } catch (IOException e) {
-            throw new IllegalArgumentException("Error occurred while processing response for Widget metadata call", e);
-        }
-        return widgets;
-    }
-
-    /**
-     * Method to parse an xml response from wookie into a set of Widget objects
-     * @param xml -a w3c dom document
-     * @return an array of widgets
-     */
-    private Widget[] parseWookieAdvert(Document xml){
-        List<Widget> widgets = new ArrayList<Widget>();
-        Widget widget;
-        Element currentElement = null;
-        Element rootEl = xml.getDocumentElement();
-        NodeList widgetNodes = rootEl.getElementsByTagName("widget");
-        if(widgetNodes != null && widgetNodes.getLength() > 0) {
-            for(int i = 0 ; i < widgetNodes.getLength();i++) {
-                widget = new Widget();
-                widget.setType(getSupportedContext());
-                // get the guid
-                Element el = (Element)widgetNodes.item(i);
-                widget.setUrl(el.getAttribute("identifier"));
-                // get the title
-                Node titleNode = el.getElementsByTagName("title").item(0);
-                if(titleNode != null){
-                    currentElement = (Element)titleNode;
-                    widget.setTitle(currentElement.getTextContent());
-                }
-                // get the description
-                Node descriptionNode = el.getElementsByTagName("description").item(0);
-                if(descriptionNode != null){
-                    currentElement = (Element)descriptionNode;
-                    widget.setDescription(currentElement.getTextContent());
-                }
-                // get the icon
-                Node iconNode = el.getElementsByTagName("icon").item(0);
-                if(iconNode != null){
-                    currentElement = (Element)iconNode;
-                    widget.setThumbnailUrl(currentElement.getTextContent());
-                }
-                // get the icon
-                Node authorNode = el.getElementsByTagName("author").item(0);
-                if(authorNode != null){
-                    currentElement = (Element)authorNode;
-                    widget.setAuthor(currentElement.getTextContent());
-                }
-                // add to the list
-                widgets.add(widget);
-            }
-        }
-        Widget[] widgetsArr = new Widget[widgets.size()];
-        widgetsArr = widgets.toArray(widgetsArr);
-        return widgetsArr;
-    }
-
 }

Modified: incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetService.java?rev=1303364&r1=1303363&r2=1303364&view=diff
==============================================================================
--- incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetService.java (original)
+++ incubator/rave/trunk/rave-providers/rave-w3c-provider/src/main/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetService.java Wed Mar 21 11:41:54 2012
@@ -29,6 +29,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
 
 public class WookieWidgetService implements WidgetProviderService {
   private static Logger logger = LoggerFactory.getLogger(WookieWidgetService.class);
@@ -55,6 +57,35 @@ public class WookieWidgetService impleme
     }
     
     /**
+     * Get all widgets available from the configured Wookie server
+     * @return an array of available widgets
+     * @throws WookieConnectorException
+     */
+    public Widget[] getWidgets() throws WookieConnectorException{
+        connectorService = getWookieConnectorService(wookieServerUrl, wookieApiKey, null);    
+    	Collection<org.apache.wookie.connector.framework.Widget> widgets = connectorService.getAvailableWidgets().values();
+    	ArrayList<Widget> raveWidgets = new ArrayList<Widget>();
+    	for (org.apache.wookie.connector.framework.Widget wookieWidget: widgets){
+    		Widget widget = new Widget();
+    		widget.setUrl(wookieWidget.getIdentifier());
+    		widget.setDescription(wookieWidget.getDescription());
+    		widget.setTitle(wookieWidget.getTitle());
+    		widget.setThumbnailUrl(wookieWidget.getIcon().toString());
+    		raveWidgets.add(widget);
+    	}
+    	return raveWidgets.toArray(new Widget[raveWidgets.size()]);
+    }
+    
+    public Widget getWidget(String url) throws WookieConnectorException{
+    	for (Widget widget: getWidgets()){
+    		if (widget.getUrl().equalsIgnoreCase(url)){
+    			return widget;
+    		}
+    	}
+    	return null;
+    }
+    
+    /**
      * Gets the Widget Instance corresponding to the RegionWidget and the Viewer
      * @param widget the type of Widget to obtain
      * @param sharedDataKey the context for data sharing
@@ -100,9 +131,7 @@ public class WookieWidgetService impleme
     
     // Get the wookie service connector
     private WookieConnectorService getWookieConnectorService(String serverURL, String apiKey, String sharedDataKey ) throws WookieConnectorException {
-      if (connectorService == null) {
-        connectorService = new WookieConnectorService(serverURL, apiKey, sharedDataKey);
-      }
+      connectorService = new WookieConnectorService(serverURL, apiKey, sharedDataKey);
       return connectorService;
     }
 

Modified: incubator/rave/trunk/rave-providers/rave-w3c-provider/src/test/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolverTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-providers/rave-w3c-provider/src/test/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolverTest.java?rev=1303364&r1=1303363&r2=1303364&view=diff
==============================================================================
--- incubator/rave/trunk/rave-providers/rave-w3c-provider/src/test/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolverTest.java (original)
+++ incubator/rave/trunk/rave-providers/rave-w3c-provider/src/test/java/org/apache/rave/provider/w3c/service/impl/WookieWidgetMetadataResolverTest.java Wed Mar 21 11:41:54 2012
@@ -19,14 +19,13 @@
 
 package org.apache.rave.provider.w3c.service.impl;
 
-import org.apache.commons.io.FileUtils;
 import org.apache.rave.portal.model.Widget;
 import org.apache.rave.portal.service.WidgetMetadataResolver;
 import org.apache.rave.provider.w3c.repository.W3CWidgetMetadataRepository;
+import org.apache.wookie.connector.framework.WookieConnectorException;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.io.File;
 import java.io.IOException;
 
 import static org.easymock.EasyMock.createNiceMock;
@@ -35,26 +34,30 @@ import static org.easymock.EasyMock.repl
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 
 public class WookieWidgetMetadataResolverTest {
     private W3CWidgetMetadataRepository wookieWidgetMetadataRepository;
     private WidgetMetadataResolver widgetMetadataResolver;
+    private WookieWidgetService widgetService;
     private static final String TYPE = "W3C";
 
     // TODO - update these tests to use GUID rather than id once rave is bundled with wookie 0.10.0
     private static final String VALID_IDENTIFIER = "7";
     private static final String VALID_GROUP_IDENTIFIER = "?all=true";
-    private static final String NO_WIDGETS_RESPONSE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><widgets></widgets>";
-    private static File ALL_WIDGETS_FILE;
-    private static File SINGLE_WIDGET_FILE;
+    private static Widget TEST_WIDGET;
 
     @Before
     public void setup() {
+    	widgetService = createNiceMock(WookieWidgetService.class);
         wookieWidgetMetadataRepository = createNiceMock(W3CWidgetMetadataRepository.class);
-        widgetMetadataResolver = new WookieWidgetMetadataResolver(wookieWidgetMetadataRepository);
-        ALL_WIDGETS_FILE = new File("src/test/resources/allwidgets.xml");
-        SINGLE_WIDGET_FILE = new File("src/test/resources/singlewidget.xml");
+        widgetMetadataResolver = new WookieWidgetMetadataResolver(widgetService);
+        
+        TEST_WIDGET = new Widget();
+        TEST_WIDGET.setTitle("freeder");
+        TEST_WIDGET.setUrl("http://wookie.apache.org/widgets/freeder");
+        TEST_WIDGET.setDescription("An RSS reader widget optimised for small screens or desktop widgets.");
+        TEST_WIDGET.setThumbnailUrl("http://localhost:8080/wookie/wservices/wookie.apache.org/widgets/freeder/images/icon.png");
+        TEST_WIDGET.setType(TYPE);
     }
 
     @Test
@@ -63,41 +66,48 @@ public class WookieWidgetMetadataResolve
     }
 
     @Test
-    public void getMetadata() throws IOException {
-        assertTrue(SINGLE_WIDGET_FILE.exists());
-        String xmlText = FileUtils.readFileToString(SINGLE_WIDGET_FILE);
-        expect(wookieWidgetMetadataRepository.getWidgetMetadata(VALID_IDENTIFIER)).andReturn(xmlText);
+    public void getMetadata() throws IOException, WookieConnectorException {
+        expect(wookieWidgetMetadataRepository.getWidgetMetadata(VALID_IDENTIFIER)).andReturn(TEST_WIDGET);
         replay(wookieWidgetMetadataRepository);
+        
+        expect(widgetService.getWidget(VALID_IDENTIFIER)).andReturn(TEST_WIDGET);
+        replay(widgetService);
+        
         Widget w = widgetMetadataResolver.getMetadata(VALID_IDENTIFIER);
         assertNotNull(w);
         assertEquals("freeder", w.getTitle());
-        assertEquals("Apache Wookie (Incubating) Team", w.getAuthor());
         assertEquals("http://wookie.apache.org/widgets/freeder", w.getUrl());
         assertEquals("An RSS reader widget optimised for small screens or desktop widgets.", w.getDescription());
         assertEquals("http://localhost:8080/wookie/wservices/wookie.apache.org/widgets/freeder/images/icon.png", w.getThumbnailUrl());
         assertEquals(TYPE, w.getType());
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void getMetadata_noWidgetFound() {
-        expect(wookieWidgetMetadataRepository.getWidgetMetadata(VALID_IDENTIFIER)).andReturn(NO_WIDGETS_RESPONSE);
+    @Test
+    public void getMetadata_noWidgetFound() throws WookieConnectorException {
+        expect(wookieWidgetMetadataRepository.getWidgetMetadata(VALID_IDENTIFIER)).andReturn(null);
         replay(wookieWidgetMetadataRepository);
+        
+        expect(widgetService.getWidget(VALID_IDENTIFIER)).andReturn(null);
+        replay(widgetService);
         Widget w = widgetMetadataResolver.getMetadata(VALID_IDENTIFIER);
-        assertNotNull(w);
+        assertNull(w);
     }
 
     @Test
-    public void getAllWidgets() throws IOException {
-        assertTrue(ALL_WIDGETS_FILE.exists());
-        String xmlText = FileUtils.readFileToString(ALL_WIDGETS_FILE);
-        expect(wookieWidgetMetadataRepository.getWidgetMetadata(VALID_GROUP_IDENTIFIER)).andReturn(xmlText);
+    public void getAllWidgets() throws IOException, WookieConnectorException {
+        Widget[] results = new Widget[]{TEST_WIDGET};
+        expect(wookieWidgetMetadataRepository.getWidgetMetadata()).andReturn(results);
         replay(wookieWidgetMetadataRepository);
+        
+        expect(widgetService.getWidgets()).andReturn(results);
+        replay(widgetService);
+        
         Widget[] widgets = widgetMetadataResolver.getMetadataGroup(VALID_GROUP_IDENTIFIER);
-        assertEquals(14, widgets.length);
-        assertNull(widgets[0].getThumbnailUrl());
-        assertEquals("http://www.getwookie.org/widgets/wiki", widgets[3].getUrl());
-        assertEquals("A silly Weather widget", widgets[4].getDescription());
-        assertEquals("Ta-Da!", widgets[9].getTitle());
-        assertEquals("http://localhost:8080/wookie/wservices/www.opera.com/widgets/bubbles/icon_64.png", widgets[12].getThumbnailUrl());
+        assertEquals(1, widgets.length);
+        assertEquals("freeder", widgets[0].getTitle());
+        assertEquals("http://wookie.apache.org/widgets/freeder", widgets[0].getUrl());
+        assertEquals("An RSS reader widget optimised for small screens or desktop widgets.", widgets[0].getDescription());
+        assertEquals("http://localhost:8080/wookie/wservices/wookie.apache.org/widgets/freeder/images/icon.png", widgets[0].getThumbnailUrl());
+        assertEquals(TYPE, widgets[0].getType());
     }
 }