class HypermediaRepositoryJaxrsService {

    @SuppressWarnings("unchecked")
	@GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, ResourceMediaType.FOLDER_XML, ResourceMediaType.FOLDER_JSON})
    public Response getResources(
            @QueryParam(RestConstants.QUERY_PARAM_SEARCH_QUERY) String q,
            @QueryParam("folderUri") String folderUri,
            @QueryParam("type") List<String> type,
            @QueryParam("excludeType") List<String> excludeType,
            @QueryParam("excludeFolder") List<String> excludeFolders,
            @QueryParam("containerType") List<String> containerType,
            @QueryParam("accessType") String accessTypeString,
            @QueryParam(RestConstants.QUERY_PARAM_OFFSET)@DefaultValue("0") Integer start,
            @QueryParam(RestConstants.QUERY_PARAM_LIMIT)@DefaultValue("100") Integer limit,
            @QueryParam("recursive") @DefaultValue("true") Boolean recursive,
            @QueryParam("showHiddenItems") @DefaultValue("false") Boolean showHiddenItems,
            @QueryParam("forceTotalCount") @DefaultValue("false") Boolean forceTotalCount,
            @QueryParam(RestConstants.QUERY_PARAM_SORT_BY) String sortBy,
            @QueryParam(RestConstants.QUERY_PARAM_EXPANDED) Boolean expanded,
            @QueryParam("forceFullPage") @DefaultValue("false") Boolean forceFullPage,
            @HeaderParam(HttpHeaders.ACCEPT) String accept,
            @Context final HttpServletRequest httpServletRequest) throws ErrorDescriptorException {

        Response res;
        if (ResourceMediaType.FOLDER_JSON.equals(accept) || ResourceMediaType.FOLDER_XML.equals(accept)) {
            res = getResourceDetails("", accept, containerType, expanded, httpServletRequest);
        } else {

            final String organizationFolderRegex = "^(?:" + configuration.getOrganizationsFolderUri() + "/([^/]+))*";
            final List<String> excludeFoldersWithPublic = new ArrayList<String>(excludeFolders);

            AccessType accessType = AccessType.ALL;
            if (accessTypeString != null && !"".equals(accessTypeString)) {
                if (AccessType.MODIFIED.name().equalsIgnoreCase(accessTypeString)) {
                    accessType = AccessType.MODIFIED;
                }
                if (AccessType.VIEWED.name().equalsIgnoreCase(accessTypeString)) {
                    accessType = AccessType.VIEWED;
                }
            }

            User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            long startLevelQuery = System.currentTimeMillis();
            if(log.isDebugEnabled()){
                for(Object o: ListUtils.union(type,  containerType)){
                	log.debug("*** HYPER *** type: " + o.toString());
                }
            }

            RepositorySearchResult<ClientResourceLookup> searchResult;
            if (containerType.contains("folder")) {
                ArrayList<String> splitContainerType = new ArrayList<String>(containerType);
                splitContainerType.remove("folder");
                searchResult =
                        batchRepositoryService.getResourcesForLookupClass(RepoFolder.class.getName(),q, folderUri, containerType, excludeType, null, excludeFolders,
                                start, limit,
                                recursive, showHiddenItems,
                                sortBy, accessType, user,
                                forceFullPage);

                int foundFolders = searchResult.getItems().size();

                searchResult.append(
                        batchRepositoryService.getResourcesForLookupClass(RepoResourceItem.class.getName(),q, folderUri, ListUtils.union(type, splitContainerType), excludeType, null, excludeFolders,
                                foundFolders>0?0:start-foundFolders<0?0:start-foundFolders, limit-foundFolders,
                                recursive, showHiddenItems,
                                sortBy, accessType, user,
                                forceFullPage)
                );

            } else {
                searchResult =
                    batchRepositoryService.getResources(q, folderUri, ListUtils.union(type, containerType), excludeType, null, excludeFolders,
	                    start, limit,
	                    recursive, showHiddenItems,
	                    sortBy, accessType, user,
	                    forceFullPage);
            }
            if(log.isDebugEnabled()){
                log.debug("*** HYPER *** level query took " + (System.currentTimeMillis() - startLevelQuery) + " (ms)");
            }

            List<HypermediaResourceLookup> hypermediaLookups = new ArrayList<HypermediaResourceLookup>(searchResult.getItems().size());
            excludeFoldersWithPublic.add(configuration.getPublicFolderUri());
            // split lookups into two groups
            List<ClientResourceLookup> lookup4excludeWithPublicFolders = new ArrayList<ClientResourceLookup>();
            List<ClientResourceLookup> lookup4excludeFolders = new ArrayList<ClientResourceLookup>();

            ClientResourceLookup parent = new ClientResourceLookup();
            parent.setUri(folderUri);

            // pre-sort
            for(ClientResourceLookup resourceLookup: searchResult.getItems()){
               if (containerType.contains(resourceLookup.getResourceType())) {
                   if (resourceLookup.getUri().matches(organizationFolderRegex)) {
                       lookup4excludeWithPublicFolders.add(resourceLookup);
                   } else {
                       lookup4excludeFolders.add(resourceLookup);
                   }
               }
            }

            // Call bulk resource count for exclude With Public
            List<Object[]> results4excludeWithPublicFolders = lookup4excludeWithPublicFolders.isEmpty()?(List<Object[]>)Collections.EMPTY_LIST:batchRepositoryService.getResourcesCount(q,
                folderUri==null?lookup4excludeWithPublicFolders:Arrays.asList(parent),
                new ArrayList<String>(type),
                excludeType,
                excludeFoldersWithPublic,
                true,
                showHiddenItems,
                accessType,
                null
            );
            // Call bulk resource count for just Resource Lookup
            List<Object[]> results4excludeFolders = lookup4excludeFolders.isEmpty()?(List<Object[]>)Collections.EMPTY_LIST:batchRepositoryService.getResourcesCount(q,
            	folderUri==null?lookup4excludeFolders:Arrays.asList(parent),
                new ArrayList<String>(type),
                excludeType,
                excludeFolders,
                true,
                showHiddenItems,
                accessType,
                null
            );

            // remove odd entries from both lists
            // by removing corresponding finding in opposite result lists
            if(!results4excludeWithPublicFolders.isEmpty()){
               for(ClientResourceLookup resource:lookup4excludeFolders){
                   String uri = resource.getUri();
                   for(Iterator<Object[]> it = results4excludeWithPublicFolders.iterator(); it.hasNext();){
                       Object[] r = it.next();
                       if(((String)r[0]).startsWith(uri)){
                           it.remove();
                       }
                   }
               }
            }
            if(!results4excludeFolders.isEmpty()){
               for(ClientResourceLookup resource:lookup4excludeWithPublicFolders){
                   String uri = resource.getUri();
                   for(Iterator<Object[]> it = results4excludeFolders.iterator(); it.hasNext();){
                       Object[] r = it.next();
                       if(((String)r[0]).startsWith(uri+Folder.SEPARATOR)){
                           it.remove();
                       }
                   }
               }
            }

            for (ClientResourceLookup resourceLookup : searchResult.getItems()) {
                HypermediaResourceLookup hypermediaResourceLookup = new HypermediaResourceLookup(resourceLookup);

                if (containerType.contains(resourceLookup.getResourceType())) {

                    String uri = resourceLookup.getUri();
                    // sum up results for each lookup element
                    boolean hasResource=false;

                    if (resourceLookup.getUri().matches(organizationFolderRegex)) {
                        for(Iterator<Object[]> it = results4excludeWithPublicFolders.iterator(); it.hasNext();){
                            Object[] r = it.next();
                            if(((String)r[0]).startsWith(uri+Folder.SEPARATOR)){
                                // optimization, we do not really need exact count, just yes or no
                                // so not really counting to squeeze little bit of performance while we can
                                hasResource = true;
                                it.remove(); // remove from the list to optimize multiple pass processing
                            }
                        }
                    } else {
                        for(Iterator<Object[]> it = results4excludeFolders.iterator(); it.hasNext();){
                            String str = (String)it.next()[0] + Folder.SEPARATOR;
                            if(str.startsWith(uri+Folder.SEPARATOR)){
                                // optimization, we do not really need exact count, just yes or no
                                // so not really counting to squeeze little bit of performance while we can
                                hasResource = true;
                                it.remove(); // remove from the list to optimize multiple pass processing
                            }
                        }
                    }

                    if (hasResource) {
                        //add links
                        HypermediaResourceLinks links = new HypermediaResourceLinks();
                        links.setSelf(requestInfoProvider.getBaseUrl() + "rest_v2/hypermedia_ext/resources" + hypermediaResourceLookup.getUri());


                        links.setContent(buildResourcesSearchLink(q, hypermediaResourceLookup.getUri(), type, excludeType,
                            containerType, accessTypeString, start, limit, recursive, showHiddenItems,
                            forceTotalCount, sortBy, expanded, forceFullPage, accept));

                        hypermediaResourceLookup.setLinks(links);
                    } else {
                        //add stubs for links
                        HypermediaResourceLinks links = new HypermediaResourceLinks();
                        links.setSelf(requestInfoProvider.getBaseUrl() + "rest_v2/hypermedia_ext/resources" + hypermediaResourceLookup.getUri());
                        links.setContent("");

                        hypermediaResourceLookup.setLinks(links);
                    }
                }
                hypermediaLookups.add(hypermediaResourceLookup);
            }

            HypermediaResourceListWrapper result = new HypermediaResourceListWrapper(hypermediaLookups);
            HypermediaResourceLookupLinks lookupLinks = new HypermediaResourceLookupLinks();

            lookupLinks.setSelf(buildResourcesSearchLink(q, folderUri, type, excludeType,
                    containerType, accessTypeString, start, limit, recursive, showHiddenItems,
                    forceTotalCount, sortBy, expanded, forceFullPage, accept));

            lookupLinks.setNext(buildResourcesSearchLink(q, folderUri, type, excludeType,
                    containerType, accessTypeString, start + 100, limit, recursive, showHiddenItems,
                    forceTotalCount, sortBy, expanded, forceFullPage, accept));

            if (start - 100 >= 0) {
                lookupLinks.setPrev(buildResourcesSearchLink(q, folderUri, type, excludeType,
                        containerType, accessTypeString, start - 100, limit, recursive, showHiddenItems,
                        forceTotalCount, sortBy, expanded, forceFullPage, accept));
            } else {
                lookupLinks.setPrev("");
            }

            result.setLinks(lookupLinks);

            final int iStart = searchResult.getClientOffset();
            final int iLimit = searchResult.getClientLimit();
            Response.ResponseBuilder response = null;

            int realResultSize = searchResult.size();
            boolean isForceTotalCount = (forceTotalCount != null && forceTotalCount);

            if (realResultSize != 0) {
                response = Response.status(Response.Status.OK).entity(result);

                response.header(RestConstants.HEADER_START_INDEX, iStart)
                        .header(RestConstants.HEADER_RESULT_COUNT, realResultSize);

                int totalCount = realResultSize;
                if (isForceTotalCount || iStart == 0 || forceFullPage) {
                    totalCount = forceFullPage
                            ? (((realResultSize < iLimit) && !isForceTotalCount) ? realResultSize : searchResult.getTotalCount())
                            : batchRepositoryService.getResourcesCount(q, folderUri, type, excludeType, excludeFolders, recursive, showHiddenItems, accessType, user);
                }

                if (iStart == 0 || isForceTotalCount || iLimit == 0 || forceFullPage) {
                    response.header(RestConstants.HEADER_TOTAL_COUNT, totalCount);
                }

                if (forceFullPage && (searchResult.getNextOffset() < searchResult.getTotalCount())) {
                    response.header(RestConstants.HEADER_NEXT_OFFSET, searchResult.getNextOffset());
                }
            } else {
                response = Response.status(Response.Status.NO_CONTENT);
            }

            res = response.build();
        }
        return res;

    }

}