/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.view.internal.filter;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.UUID;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.eclipse.skalli.commons.CollectionUtils;
import org.eclipse.skalli.commons.Link;
import org.eclipse.skalli.ext.mapping.scm.ScmLocationMapper;
import org.eclipse.skalli.model.Project;
import org.eclipse.skalli.model.User;
import org.eclipse.skalli.model.ext.devinf.DevInfProjectExt;
import org.eclipse.skalli.services.Services;
import org.eclipse.skalli.services.entity.EntityServices;
import org.eclipse.skalli.services.favorites.Favorites;
import org.eclipse.skalli.services.favorites.FavoritesService;
import org.eclipse.skalli.services.project.ProjectService;
import org.eclipse.skalli.services.search.SearchHit;
import org.eclipse.skalli.services.search.SearchResult;
import org.eclipse.skalli.services.search.SearchService;
import org.eclipse.skalli.services.template.ProjectTemplate;
import org.eclipse.skalli.services.template.ProjectTemplateService;
import org.eclipse.skalli.view.Consts;
import org.eclipse.skalli.view.LoginUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractSearchFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(AbstractSearchFilter.class);
public static final String ATTRIBUTE_RESULTSIZE = "resultSize"; //$NON-NLS-1$
public static final String ATTRIBUTE_DURATION = "duration"; //$NON-NLS-1$
public static final String ATTRIBUTE_VIEWSIZE = "viewSize"; //$NON-NLS-1$
public static final String ATTRIBUTE_TITLE = "title"; //$NON-NLS-1$
public static final String ATTRIBUTE_PROJECTS = "projects"; //$NON-NLS-1$
public static final String ATTRIBUTE_NATURES = "natures"; //$NON-NLS-1$
public static final String ATTRIBUTE_PARENTS = "parents"; //$NON-NLS-1$
public static final String ATTRIBUTE_PARENTCHAINS = "parentChains"; //$NON-NLS-1$
public static final String ATTRIBUTE_SUBPROJETS = "subprojects"; //$NON-NLS-1$
public static final String ATTRIBUTE_SOURCELINKS = "sourceLinks"; //$NON-NLS-1$
public static final String ATTRIBUTE_START = "start"; //$NON-NLS-1$
public static final String ATTRIBUTE_CURRENTPAGE = "currentPage"; //$NON-NLS-1$
public static final String ATTRIBUTE_PAGES = "pages"; //$NON-NLS-1$
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
// retrieve the logged-in user
String userId = (String) request.getAttribute(Consts.ATTRIBUTE_USERID);
User user = (User) request.getAttribute(Consts.ATTRIBUTE_USER);
// calculate start param
int start = toInt(request.getParameter(Consts.PARAM_START), 0, -1);
// calculate count size param
int count = toInt(request.getParameter(Consts.PARAM_COUNT), 10, 50);
// retrieve search hits and based on that parent projects and subprojects
SearchResult<Project> searchResult = getSearchHits(user, request, response, start, count);
List<SearchHit<Project>> searchHits = searchResult.getResult();
Map<String, String> natures = getProjectNatures(searchHits);
Map<String, Project> parents = getParents(searchHits);
Map<String, List<Project>> parentChains = getParentChains(searchHits);
Map<String, List<SearchHit<Project>>> subprojects = getSubprojects(searchHits);
Map<String, List<String>> sourceLinks = getSourceLinks(userId, searchHits);
// retrieve the favorites of the user
Favorites favorites = getFavorites(user);
// calculate params for pager
int resultSize = searchResult.getResultCount();
int pages = (int) Math.ceil((double) resultSize / (double) count);
int currentPage = (int) Math.floor((double) start / (double) count) + 1;
long duration = searchResult.getDuration();
// set the request attributes
request.setAttribute(ATTRIBUTE_TITLE, getTitle(user));
request.setAttribute(ATTRIBUTE_PROJECTS, searchHits);
request.setAttribute(ATTRIBUTE_NATURES, natures);
request.setAttribute(ATTRIBUTE_PARENTS, parents);
request.setAttribute(ATTRIBUTE_PARENTCHAINS, parentChains);
request.setAttribute(ATTRIBUTE_SUBPROJETS, subprojects);
request.setAttribute(ATTRIBUTE_SOURCELINKS, sourceLinks);
request.setAttribute(Consts.ATTRIBUTE_FAVORITES, favorites.asMap());
request.setAttribute(ATTRIBUTE_DURATION, duration);
request.setAttribute(ATTRIBUTE_START, start);
request.setAttribute(ATTRIBUTE_VIEWSIZE, count);
request.setAttribute(ATTRIBUTE_RESULTSIZE, resultSize);
request.setAttribute(ATTRIBUTE_CURRENTPAGE, currentPage);
request.setAttribute(ATTRIBUTE_PAGES, pages);
request.setAttribute(Consts.ATTRIBUTE_USER, user);
if (((HttpServletRequest) request).getPathInfo() == null) {
request.getRequestDispatcher(Consts.JSP_SEARCHRESULT).forward(request, response);
return;
}
// proceed along the chain
chain.doFilter(request, response);
}
/**
* Returns the next set of search hits with size <code>viewSize</code> beginning
* with the entry whose index is <code>start</code>.
*
* @param user
* the logged-in user, or <code>null</code> for an anonymous user.
* @param request
* the servlet request.
* @param response
* the servlet response (can be used for error handling).
* @param start
* the index of the first search hit to return.
* @param count
* the number of search hits to return.
*/
protected abstract SearchResult<Project> getSearchHits(User user, ServletRequest request, ServletResponse response,
int start, int count) throws IOException, ServletException;
/**
* Returns <code>true</code> if for components the nearest project in
* the hierarchy should be shown.
*
* @param user
* the logged-in user, or <code>null</code> for an anonymous user.
* @param request
* the servlet request.
* @param response
* the servlet response (can be used for error handling).
*/
protected abstract boolean showNearestProjects(User user, ServletRequest request, ServletResponse response);
/**
* Returns the title of the search result window.
* This method always returns "Search Result". Derived filters should overwrite this method.
*
* @param user
* the logged-in user, or <code>null</code> for an anonymous user.
*/
protected String getTitle(User user) {
return "Search Result";
}
protected ProjectService getProjectService() {
return ((ProjectService)EntityServices.getByEntityClass(Project.class));
}
protected ProjectTemplateService getProjectTemplateService() {
return Services.getRequiredService(ProjectTemplateService.class);
}
protected SearchService getSearchService() {
return Services.getRequiredService(SearchService.class);
}
protected FavoritesService getFavoritesService() {
return Services.getService(FavoritesService.class);
}
protected User getUser(HttpServletRequest request) {
LoginUtils util = new LoginUtils(request);
return util.getLoggedInUser();
}
/**
* Returns the favorites of the given user. For anonymous users
* the method returns {@link Favorites#Favorites()}.
*
* @param user
* the logged-in user, or <code>null</code> for an anonymous user.
*/
protected Favorites getFavorites(User user) {
if (user == null) {
return new Favorites();
}
FavoritesService favoritesService = getFavoritesService();
if (favoritesService == null) {
return new Favorites(user.getUserId());
}
return favoritesService.getFavorites(user.getUserId());
}
/**
* Returns a map of the natures of the given project search hits.
* Key: UUID.toString() of a project in searchHits; Value: ProjectNature.toString() of that project.
*/
protected Map<String, String> getProjectNatures(List<SearchHit<Project>> searchHits) {
ProjectTemplateService templateService = getProjectTemplateService();
Map<String, String> natures = new HashMap<String, String>();
for (SearchHit<Project> searchHit : searchHits) {
Project project = searchHit.getEntity();
if (project != null) {
String uuid = project.getUuid().toString();
ProjectTemplate template = templateService.getProjectTemplateById(project.getProjectTemplateId());
if (template != null) {
natures.put(uuid, template.getProjectNature().toString());
}
}
}
return natures;
}
protected Map<String, List<String>> getSourceLinks(String userId, List<SearchHit<Project>> searchHits) {
Map<String, List<String>> links = new HashMap<String, List<String>>();
for (SearchHit<Project> searchHit : searchHits) {
Project project = searchHit.getEntity();
if (project != null) {
String uuid = project.getUuid().toString();
DevInfProjectExt devInf = project.getExtension(DevInfProjectExt.class);
if (devInf != null && CollectionUtils.isNotBlank(devInf.getScmLocations())) {
ScmLocationMapper mapper = new ScmLocationMapper(ScmLocationMapper.ALL_PROVIDERS,
ScmLocationMapper.PURPOSE_BROWSE);
for (String scmLocation : devInf.getScmLocations()) {
List<String> scmUrls = new ArrayList<String>();
List<Link> mappedLinks = mapper.getMappedLinks(scmLocation, userId, project);
for (Link link: mappedLinks) {
scmUrls.add(link.getUrl());
}
if (scmUrls.isEmpty()) {
String scmUrl = searchHit.getSingleValues().get("scmUrl"); //$NON-NLS-1$
if (scmUrl != null) {
scmUrls.add(scmUrl);
}
}
links.put(uuid, scmUrls);
}
}
}
}
return links;
}
/**
* Returns a map with the direct parent projects of the given search hits.
* Key: UUID.toString() of a project in searchHits; Value: SearchHit of that project.
*/
protected Map<String, Project> getParents(List<SearchHit<Project>> searchHits) {
ProjectService projectService = getProjectService();
Map<String, Project> parents = new HashMap<String, Project>();
for (SearchHit<Project> searchHit : searchHits) {
Project project = searchHit.getEntity();
String uuid = project.getUuid().toString();
Project parent = projectService.getByUUID(project.getParentEntityId());
if (parent != null) {
parents.put(uuid, parent);
}
}
return parents;
}
protected Map<String, List<Project>> getParentChains(List<SearchHit<Project>> searchHits) {
ProjectService projectService = getProjectService();
Map<String, List<Project>> ret = new HashMap<String, List<Project>>();
for (SearchHit<Project> searchHit : searchHits) {
Project project = searchHit.getEntity();
List<Project> parentChain = projectService.getParentChain(project.getUuid());
if (parentChain.size() > 0) {
parentChain.remove(0);
}
Collections.reverse(parentChain);
ret.put(project.getUuid().toString(), parentChain);
}
return ret;
}
/**
* Returns a map with the subprojects of the given search hits.
* Key: UUID.toString() of a project in searchHits; Value: list of SearchHits of subprojects of that project.
*/
protected Map<String, List<SearchHit<Project>>> getSubprojects(List<SearchHit<Project>> searchHits) {
SearchService searchService = getSearchService();
Map<String, List<SearchHit<Project>>> subrojects = new HashMap<String, List<SearchHit<Project>>>();
for (SearchHit<Project> searchHit : searchHits) {
Project project = searchHit.getEntity();
UUID uuid = project.getUuid();
SortedSet<Project> subprojects = project.getSubProjects();
if (subprojects.size() > 0) {
subrojects.put(uuid.toString(), searchService.asSearchHits(subprojects));
}
}
return subrojects;
}
/**
* Tries to convert the request parameter into an <code>int</code>.
*
* If unsuccessful or above the limit the default value will be returned.
*/
private int toInt(String requestParameter, int defaultValue, int limit) {
if (StringUtils.isBlank(requestParameter)) {
return defaultValue;
}
try {
int value = Integer.parseInt(requestParameter);
return (limit > -1 && value > limit) ? defaultValue : value;
} catch (NumberFormatException e) {
LOG.info(MessageFormat.format("''{0}'' is not a valid number, using default: {1}", requestParameter,
defaultValue));
return defaultValue;
}
}
@Override
public void init(FilterConfig paramFilterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}