argendata.web.controller.SearchController.java Source code

Java tutorial

Introduction

Here is the source code for argendata.web.controller.SearchController.java

Source

package argendata.web.controller;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import argendata.model.FacetAppResponse;
import argendata.model.FacetCount;
import argendata.model.FacetDatasetResponse;
import argendata.model.FacetPair;
import argendata.model.OrderType;
import argendata.model.dcat.Dataset;
import argendata.service.AppService;
import argendata.service.DatasetService;
import argendata.util.Messages;
import argendata.util.Parsing;
import argendata.util.Properties;

@Controller
public class SearchController {

    private AppService appService;
    private DatasetService datasetService;
    private Properties properties;
    private Messages messages;
    private final Logger logger = Logger.getLogger(SearchController.class);

    @Autowired
    public SearchController(AppService appService, DatasetService datasetService, Properties properties,
            Messages messages) {

        this.properties = properties;
        this.appService = appService;
        this.datasetService = datasetService;
        this.messages = messages;

    }

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView searchAppMain() {
        ModelAndView mav = new ModelAndView();

        List<FacetPair> resp = new ArrayList<FacetPair>();
        List<FacetCount> solrKeywords = appService.getIndexAllAppKeywords();

        for (FacetCount c : solrKeywords) {
            resp.add(new FacetPair(c.getName(), (int) c.getCount(), null, null));
        }

        mav.addObject("myapps", resp);
        return mav;

    }

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView searchDataHome() {
        ModelAndView mav = new ModelAndView();

        List<FacetPair> respKw = new ArrayList<FacetPair>();
        List<FacetPair> respFormat = new ArrayList<FacetPair>();
        List<FacetPair> respRt = new ArrayList<FacetPair>();

        List<Count> solrKeywords = null;
        List<Count> solrFormats = null;
        List<Count> solrLocations = null;
        try {
            solrKeywords = datasetService.getAllIndexKeywords();
            solrFormats = datasetService.getAllIndexFormats();
            solrLocations = datasetService.getIndexAllLocations();
        } catch (MalformedURLException e) {
            logger.error("No se han podido obtener datos del servicio. " + e.getMessage());
            mav.setViewName("redirect:../main/home");
            return mav;
        } catch (SolrServerException e) {
            logger.error("No se han podido obtener datos del servicio. " + e.getMessage());
            mav.setViewName("redirect:../main/home");
            return mav;
        }
        if (solrKeywords != null) {
            for (Count c : solrKeywords) {
                respKw.add(new FacetPair(c.getName(), (int) c.getCount(), null, null));
            }
        }

        if (solrFormats != null) {
            for (Count c : solrFormats) {
                respFormat.add(new FacetPair(c.getName(), (int) c.getCount(), null, null));
            }
        }
        if (solrLocations != null) {
            for (Count c : solrLocations) {
                respRt.add(new FacetPair(c.getName(), (int) c.getCount(), null, null));
            }
        }

        mav.addObject("listKeywords", respKw);
        mav.addObject("listFormats", respFormat);
        mav.addObject("listLocations", respRt);
        return mav;

    }

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView search(@RequestParam(value = "type", required = false) String type,
            @RequestParam(value = "terms", required = false) String terms,
            @RequestParam(value = "page", required = false) String page,
            @RequestParam(value = "sortBy", required = false) String sortBy,
            @RequestParam(value = "resPerPage", required = false) String resPerPage,
            @RequestParam(value = "keyword", required = false) String keyword,
            @RequestParam(value = "filters", required = false) String filters) {

        String[] queryFacetsFields = { "dataQuality", "license", "modified", "spatial", "temporal", "keyword",
                "publisher", "resourceType", "format", "size", "location" };

        ModelAndView mav = new ModelAndView();

        /* Parsing things */
        if (!(type == null || type.equals("dataset") || type.equals("app") || type.equals(""))) {
            mav.setViewName("redirect:../static/error");
            return mav;

        }
        String myTerms;
        if (keyword == null)
            keyword = "";
        if (type == null || type.equals("")) {
            type = "dataset";
        }

        if (type.equals("dataset")) {
            if (terms == null) {
                mav.setViewName("redirect:/bin/search/searchDataHome.jsp");
                return mav;
            }
            if (filters == null || filters.equals("null")) {
                filters = "";
            }
            if (filters.startsWith(","))
                filters = filters.substring(1);
        }
        myTerms = Parsing.parseTerms(terms, type);
        String myPage = Parsing.parsePage(page);
        List<String> mySortByFields = Parsing.parseSortBy(sortBy);
        String myResPerPage = Parsing.parseResultsPerPage(resPerPage);
        List<String> myKeywords = Parsing.parseKeyword(keyword);
        Map<String, String> filterValuesMap = type.equals("dataset") ? Parsing.parseDatesetFilter(filters) : null;
        /* The end of parsing */
        String actualURL = properties.getMainURL() + "/search/search?type=" + type + "&terms=" + myTerms;

        Integer resultsPerPage = new Integer(myResPerPage);
        String actualPage = "&page=" + myPage;
        String actualSort = "&sortBy=" + sortBy;
        String actualResPerPage = "&resPerPage=" + resultsPerPage;
        String actualKeyword = "&keyword=" + keyword;
        String actualFilters = type.equals("dataset") ? "&filters=" + filters : null;
        /* Estas variables solo se deben usar para el caso de Dataset */
        String urlWithoutTerm = "&filters=" + filters + "&page=" + page + "&sortBy=" + sortBy + "&resPerPage="
                + resPerPage + "&keyword=" + keyword;
        try {
            /*----------------URL Things and some objects are push--------------------------*/
            List<OrderType> orderAttributesList = this.sortByProcess(type, mySortByFields, actualResPerPage,
                    actualKeyword, actualURL, actualFilters);
            mav.addObject("orderAttributesList", orderAttributesList);

            String urlWithoutPage;

            if (type.equals("dataset")) {
                urlWithoutPage = actualURL + actualFilters + actualSort + "&page=";
            } else {
                urlWithoutPage = actualURL + actualSort + "&page=";
            }

            /* Obtengo la respuesta de la query */
            FacetAppResponse appResponse = null;
            FacetDatasetResponse datasetResponse = null;
            int objAmount;
            if (type.equals("dataset")) {
                String thePublisher = filterValuesMap.get("publisher");
                mav.addObject("thePublisher", thePublisher);
                mav.addObject("publisherSelected", this.messages.getPublisherSelected());
            }
            if (type.equals("app")) {
                /* Caso app */
                appResponse = appService.searchOnIndexFacetApp(myTerms, mySortByFields, myKeywords);
                objAmount = appResponse.size();
            } else {
                /* Caso dataset */
                datasetResponse = datasetService.searchOnIndexFacetDataset(myTerms, queryFacetsFields,
                        filterValuesMap, mySortByFields, myKeywords);
                objAmount = datasetResponse.size();
            }

            /* Number page stuff */
            List<String> numberList = new ArrayList<String>();
            int pagesAmount = objAmount / resultsPerPage;
            int pageNumber = new Integer(myPage);

            /*
             * numberList: esta la lista de los numeros de pagina, de acuerdo a
             * cuantas paginas haya. Siempre va a haber al menos una. El for,
             * empieza desde 1, porque siempre va a haber una pgina, entonces
             * esa no la cuento.
             */
            numberList.add((1) + "");
            for (int i = 1; i <= pagesAmount; i++) {
                numberList.add((i + 1) + "");
            }
            /*
             * Si por la url pusieron un numero de pgina mayor, ac me redirije
             * a la pagina 1
             */
            if (pageNumber > numberList.size()) {
                actualPage = "&page=1";
                pageNumber = 1;
            }

            /* Esto era para un caso limite que no me acuerdo bien */
            if ((pagesAmount * resultsPerPage) > (objAmount - 1))
                numberList.remove(pagesAmount);

            /*
             * minIndex y maxIndex te dice los ndices de los datasets para
             * mostrar. O sea, si ests en la pgina 2 y mostrs 5 datasets por
             * pgina, el min ndex sera el 6 y el maxIndex el 10.
             */
            int minIndex = (pageNumber - 1) * resultsPerPage;
            int maxIndex = minIndex + resultsPerPage;
            if (maxIndex > objAmount)
                maxIndex = objAmount;

            /*
             * Ac se usan los minIndex y maxIndex para obtener los datasets, y
             * adems se calcula cual es la pgina previa y siguiente
             */

            mav.addObject("cantResults", objAmount);

            if (objAmount > 0) {
                if (type.equals("dataset")) {
                    if (!terms.equals("*") || !(filters.equals("")) || !(keyword.equals(""))) {
                        mav.addObject("datasets", datasetResponse.getDatasets().subList(minIndex, maxIndex));
                        mav.addObject("numberList", numberList);

                        if ((pageNumber - 1) > 0)
                            mav.addObject("prevPage", urlWithoutPage + (pageNumber - 1) + actualResPerPage);

                        if ((pageNumber * resultsPerPage) <= (objAmount - 1))
                            mav.addObject("nextPage", urlWithoutPage + (pageNumber + 1) + actualResPerPage);
                        mav.addObject("showingMin", minIndex + 1);
                        mav.addObject("showingMax", maxIndex);
                    }
                } else {
                    if ((pageNumber - 1) > 0)
                        mav.addObject("prevPage", urlWithoutPage + (pageNumber - 1) + actualResPerPage);

                    if ((pageNumber * resultsPerPage) <= (objAmount - 1))
                        mav.addObject("nextPage", urlWithoutPage + (pageNumber + 1) + actualResPerPage);

                    /*-------------------------------*/
                    mav.addObject("apps", appResponse.getApps().subList(minIndex, maxIndex));
                    mav.addObject("showingMin", minIndex);
                    mav.addObject("showingMax", maxIndex);
                }
            }

            List<FacetPair> currentFilters = new ArrayList<FacetPair>();
            // APP
            // if (objAmount > 0) {
            if (type.equals("app")) {
                mav.addObject("keywordsList",
                        getKeywords(urlWithoutPage + page + "&resPerPage=" + myResPerPage + "&keyword=", myKeywords,
                                appResponse.getKeywords(), currentFilters, keyword));
                mav.addObject("currentFiltersList", currentFilters);
            } else {

                // Dataset
                /* Aca se arma la lista de filtros */
                String[] fieldsToMap = { "dataQuality", "license", "spatial", "temporal", "publisher",
                        "resourceType", "format", "location" };

                /*
                 * Parecido a getFacetPairs pero con los keywords. Se necesita
                 * de una funcin especial porque el manejo de los keywords es
                 * diferente del de los campos comunes
                 */
                mav.addObject("keywordsList",
                        getKeywords(urlWithoutPage + page + "&resPerPage=" + myResPerPage + "&keyword=", myKeywords,
                                datasetResponse.getFacetCountValues("keyword"), currentFilters, keyword));

                StringBuffer beforeFilter = new StringBuffer("");
                StringBuffer afterFilter = new StringBuffer("");

                for (String field : fieldsToMap) {
                    mav.addObject(field + "List",
                            getFacetPairsList(field, datasetResponse.getFacetCountValues(field), filterValuesMap,
                                    actualFilters, actualSort, currentFilters, actualURL, actualResPerPage,
                                    actualKeyword, beforeFilter, afterFilter));
                }
                getPublishersURL("", null, filterValuesMap, actualFilters, actualSort, currentFilters, actualURL,
                        actualResPerPage, actualKeyword, beforeFilter, afterFilter);

                mav.addObject("beforeFilter", beforeFilter);
                mav.addObject("afterFilter", afterFilter);
                /*
                 * Years which are filtered Parecido a getFacetPairs pero con
                 * los modified. Se necesita de una funcin especial porque el
                 * manejo de los modified (Fechas) es diferente del de los
                 * campos comunes
                 */
                mav.addObject("modifiedList", getModifDates("modified", getYearsList(), filterValuesMap,
                        actualFilters, actualSort, currentFilters, actualURL, actualResPerPage, actualKeyword));

                mav.addObject("extentionList", getModifDates("size", getExtentionList(), filterValuesMap,
                        actualFilters, actualSort, currentFilters, actualURL, actualResPerPage, actualKeyword));
                mav.addObject("currentFiltersList", currentFilters);

            }
            mav.addObject("notFound", this.messages.getNoResults());
            if (type.equals("dataset")) {
                mav.addObject("urlWithoutTerm", urlWithoutTerm);
            }
            mav.addObject("actualResPerPage", actualResPerPage);
            mav.addObject("type", type);
            mav.addObject("searchTerm", terms);
            mav.addObject("actualURL", actualURL);
            mav.addObject("actualPage", actualPage);
            mav.addObject("pageNumber", page);
            mav.addObject("resultsPerPage", myResPerPage);
            mav.addObject("urlWithoutPage", urlWithoutPage);
            mav.addObject("urlWithoutResPerPage", urlWithoutPage + 1 + "&resPerPage=");
            mav.addObject("actualKeyword", actualKeyword);
            mav.addObject("numberList", numberList);
        } catch (SolrServerException e) {
            logger.error(e.getMessage());
            mav.setViewName("redirect:/bin/search/search?terms=" + terms);

        } catch (MalformedURLException e) {
            logger.error(e.getMessage());
            mav.setViewName("redirect:/bin/search/search?terms=" + terms);
        }
        return mav;
    }

    private List<String> getYearsList() {
        List<String> yearsList = new ArrayList<String>();

        yearsList.add(Integer.toString(Calendar.getInstance().get(Calendar.YEAR)));
        yearsList.add(Integer.toString(Calendar.getInstance().get(Calendar.YEAR) - 1));
        yearsList.add(Integer.toString(Calendar.getInstance().get(Calendar.YEAR) - 7) + " hasta la actualidad");
        yearsList.add("Todos los datasets");

        return yearsList;
    }

    private List<String> getExtentionList() {
        List<String> extentionList = new ArrayList<String>();

        extentionList.add("Hasta 128kb");
        extentionList.add("Entre 128kb y 512kb");
        extentionList.add("Mas de 512kb");
        extentionList.add("Indefinido");

        return extentionList;
    }

    private List<FacetPair> getModifDates(String facet, List<String> yearsList, Map<String, String> filterValuesMap,
            String actualFilters, String actualSort, List<FacetPair> currentFilters, String actualURL,
            String actualResPerPage, String actualKeyword) {

        List<FacetPair> modifiedList = new ArrayList<FacetPair>();
        if (filterValuesMap.containsKey(facet)) {
            String modifiedValue = filterValuesMap.get(facet);
            String newFiltersURL = "&filters=";
            for (String c : yearsList) {
                if (c.equals(modifiedValue)) {
                    /*
                     * Armo la URL nueva sin el filtro correspondiente a este
                     * Facet
                     */
                    for (String key : filterValuesMap.keySet()) {
                        if (!key.equals(facet)) {
                            newFiltersURL = newFiltersURL.concat(key + ":" + filterValuesMap.get(key) + ",");
                        }
                    }
                    if (newFiltersURL.endsWith(","))
                        newFiltersURL = newFiltersURL.substring(0, newFiltersURL.length() - 1);
                    modifiedList.add(new FacetPair("(-)" + c, 0,
                            actualURL + newFiltersURL + actualSort + "&page=1" + actualResPerPage + actualKeyword,
                            c));
                    currentFilters.add(new FacetPair("(-)" + c, 0,
                            actualURL + newFiltersURL + actualSort + "&page=1" + actualResPerPage + actualKeyword,
                            c));

                }

            }
        } else {
            for (String c : yearsList) {
                if (actualFilters.endsWith("="))
                    modifiedList.add(new FacetPair(">>" + c, 0, actualURL + actualFilters + facet + ":" + c
                            + actualSort + "&page=1" + actualResPerPage + actualKeyword, c));
                else
                    modifiedList.add(new FacetPair(">>" + c, 0, actualURL + actualFilters + "," + facet + ":" + c
                            + actualSort + "&page=1" + actualResPerPage + actualKeyword, c));
            }
        }

        return modifiedList;
    }

    private List<FacetPair> getKeywords(String urlWithoutKeyword, List<String> listKeywords, List<FacetCount> list,
            List<FacetPair> currentFilters, String currentKeywords) {

        List<FacetPair> modifiedList = new ArrayList<FacetPair>();
        String actualKeywordURL = "&keyword=" + currentKeywords;
        String newKeywordURL = "&keyword=";

        if (list == null)
            return modifiedList;

        for (FacetCount c : list) {
            if (listKeywords.contains(c.getName())) {

                newKeywordURL = "";
                /* Armo la nueva URL, sin este parmetro */
                for (String str : listKeywords) {
                    if (!(c.getName().equals(str)))
                        newKeywordURL = newKeywordURL.concat(str + ",");
                }
                if (newKeywordURL.endsWith(","))
                    newKeywordURL = newKeywordURL.substring(0, newKeywordURL.length() - 1);
                if (c.getCount() != 0)
                    modifiedList.add(new FacetPair("(-)" + c.getName(), (int) c.getCount(),
                            urlWithoutKeyword + newKeywordURL, c.getName()));
                currentFilters.add(new FacetPair("(-)" + c.getName(), (int) c.getCount(),
                        urlWithoutKeyword + newKeywordURL, c.getName()));
            } else {

                if (actualKeywordURL.endsWith("=")) {
                    if (c.getCount() != 0)
                        modifiedList.add(new FacetPair(">>" + c.getName(), (int) c.getCount(),
                                urlWithoutKeyword + c.getName(), c.getName()));
                } else {
                    if (c.getCount() != 0)
                        modifiedList.add(new FacetPair(">>" + c.getName(), (int) c.getCount(),
                                urlWithoutKeyword + currentKeywords + "," + c.getName(), c.getName()));
                }
            }

        }

        return modifiedList;
    }

    private void getPublishersURL(String facet, List<FacetCount> list, Map<String, String> filterValuesMap,
            String actualFilters, String actualSort, List<FacetPair> currentFilters, String actualURL,
            String actualResPerPage, String actualKeyword, StringBuffer beforeFilter, StringBuffer afterFilter) {

        String newFiltersURL = "&filters=";
        for (String key : filterValuesMap.keySet()) {
            newFiltersURL = newFiltersURL.concat(key + ":" + filterValuesMap.get(key) + ",");
        }
        if (newFiltersURL.endsWith(","))
            newFiltersURL = newFiltersURL.substring(0, newFiltersURL.length() - 1);
        if (newFiltersURL.charAt(newFiltersURL.length() - 1) == '=') {
            beforeFilter.append(actualURL + newFiltersURL.concat("publisher:"));
        } else
            beforeFilter.append(actualURL + newFiltersURL.concat(",publisher:"));
        afterFilter.append(actualSort + "&page=1" + actualResPerPage + actualKeyword);
        return;
    }

    private List<FacetPair> getFacetPairsList(String facet, List<FacetCount> list,
            Map<String, String> filterValuesMap, String actualFilters, String actualSort,
            List<FacetPair> currentFilters, String actualURL, String actualResPerPage, String actualKeyword,
            StringBuffer beforeFilter, StringBuffer afterFilter) {

        List<FacetPair> modifiedList = new ArrayList<FacetPair>();
        if (list == null)
            return modifiedList;
        boolean changeFlag = false;
        if (filterValuesMap.containsKey(facet)) {
            String modifiedValue = filterValuesMap.get(facet);
            String newFiltersURL = "&filters=";

            if (facet.equals("publisher")) {
                /*
                 * Armo la URL nueva sin el filtro correspondiente a este Facet
                 */
                for (String key : filterValuesMap.keySet()) {
                    if (!key.equals(facet)) {
                        newFiltersURL = newFiltersURL.concat(key + ":" + filterValuesMap.get(key) + ",");
                    }
                }

                if (newFiltersURL.endsWith(","))
                    newFiltersURL = newFiltersURL.substring(0, newFiltersURL.length() - 1);
                modifiedList.add(new FacetPair("(-)" + filterValuesMap.get(facet), (int) list.get(0).getCount(),
                        actualURL + newFiltersURL + actualSort + "&page=1" + actualResPerPage + actualKeyword,
                        filterValuesMap.get(facet)));
                currentFilters.add(new FacetPair("(-)" + filterValuesMap.get(facet), (int) list.get(0).getCount(),
                        actualURL + newFiltersURL + actualSort + "&page=1" + actualResPerPage + actualKeyword,
                        filterValuesMap.get(facet)));

            } else {

                for (FacetCount c : list) {
                    if (c.getName().equals(modifiedValue)) {
                        /*
                         * Armo la URL nueva sin el filtro correspondiente a
                         * este Facet
                         */
                        for (String key : filterValuesMap.keySet()) {
                            if (!key.equals(facet)) {
                                newFiltersURL = newFiltersURL.concat(key + ":" + filterValuesMap.get(key) + ",");
                            }
                        }
                    }

                    if (newFiltersURL.endsWith(","))
                        newFiltersURL = newFiltersURL.substring(0, newFiltersURL.length() - 1);
                    if (c.getCount() != 0)
                        modifiedList
                                .add(new FacetPair(
                                        "(-)" + c.getName(), (int) c.getCount(), actualURL + newFiltersURL
                                                + actualSort + "&page=1" + actualResPerPage + actualKeyword,
                                        c.getName()));
                    if (c.getName().equals(filterValuesMap.get(facet)))
                        currentFilters
                                .add(new FacetPair(
                                        "(-)" + c.getName(), (int) c.getCount(), actualURL + newFiltersURL
                                                + actualSort + "&page=1" + actualResPerPage + actualKeyword,
                                        c.getName()));
                }
            }
        } else {
            for (FacetCount c : list) {
                if (actualFilters.endsWith("=")) {
                    if (c.getCount() != 0)
                        modifiedList.add(new FacetPair(
                                ">>" + c.getName(), (int) c.getCount(), actualURL + actualFilters + facet + ":"
                                        + c.getName() + actualSort + "&page=1" + actualResPerPage + actualKeyword,
                                c.getName()));
                } else {
                    if (c.getCount() != 0)
                        modifiedList.add(new FacetPair(">>" + c.getName(), (int) c.getCount(),
                                actualURL + actualFilters + "," + facet + ":" + c.getName() + actualSort + "&page=1"
                                        + actualResPerPage + actualKeyword,
                                c.getName()));
                }
            }
        }
        return modifiedList;
    }

    private List<OrderType> sortByProcess(String type, List<String> mySortByFields, String actualResPerPage,
            String actualKeyword, String actualURL, String actualFilters) {
        List<OrderType> orderAttributesList = new ArrayList<OrderType>();

        OrderType orderType;
        /*
         * Inicio de procesamiento de sortby, se sospecha que puede llegar a
         * juntarse los dos casos
         */
        if (type.equals("app")) {

            orderType = new OrderType();
            orderType.setShowName(mySortByFields.get(0));
            orderType.setName(mySortByFields.get(1));
            String orderArg = mySortByFields.get(2).equals("a") ? ":desc" : ":asc";
            orderType.setDestURL(actualURL + "&sortBy=" + mySortByFields.get(1) + orderArg + "&page=1"
                    + actualResPerPage + actualKeyword);

            orderAttributesList.add(orderType);

        } else {

            int cantOrders = mySortByFields.size();
            for (int i = 0; i < cantOrders; i += 3) {
                orderType = new OrderType();
                orderType.setShowName(mySortByFields.get(i));
                orderType.setName(mySortByFields.get(i + 1));

                String ord = mySortByFields.get(i + 2).equals("a") ? ":desc" : ":asc";
                String sortFields = "";
                String destUrl = actualURL + actualFilters + "&sortBy=" + mySortByFields.get(i + 1) + ord;
                for (int j = 0; j < mySortByFields.size(); j += 3) {
                    if (j != i) {
                        sortFields = sortFields.concat("," + mySortByFields.get(j + 1));
                        if (mySortByFields.get(j + 2).equals("a"))
                            sortFields = sortFields.concat(":asc");
                        else
                            sortFields = sortFields.concat(":desc");
                    }
                }
                destUrl = destUrl.concat(sortFields + "&page=1" + actualResPerPage + actualKeyword);
                orderType.setDestURL(destUrl);
                orderAttributesList.add(orderType);
            }
        }
        return orderAttributesList;
    }

}