Java tutorial
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Content Registry 2.0. * * The Initial Owner of the Original Code is European Environment * Agency. Portions created by Tieto Eesti are Copyright * (C) European Environment Agency. All Rights Reserved. * * Contributor(s): * Aleksandr Ivanov, Tieto Eesti */ package eionet.cr.web.action; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import net.sourceforge.stripes.action.DefaultHandler; import net.sourceforge.stripes.action.ForwardResolution; import net.sourceforge.stripes.action.RedirectResolution; import net.sourceforge.stripes.action.Resolution; import net.sourceforge.stripes.action.StreamingResolution; import net.sourceforge.stripes.action.UrlBinding; import org.apache.commons.lang.StringUtils; import eionet.cr.common.Predicates; import eionet.cr.dao.DAOException; import eionet.cr.dao.DAOFactory; import eionet.cr.dao.HelperDAO; import eionet.cr.dao.SearchDAO; import eionet.cr.dto.SearchResultDTO; import eionet.cr.dto.SubjectDTO; import eionet.cr.util.Pair; import eionet.cr.util.SortOrder; import eionet.cr.util.SortStringPair; import eionet.cr.util.SortingRequest; import eionet.cr.util.URIUtil; import eionet.cr.util.Util; import eionet.cr.util.export.ExportFormat; import eionet.cr.util.export.Exporter; import eionet.cr.util.export.XlsExporter; import eionet.cr.util.pagination.PagingRequest; import eionet.cr.web.action.factsheet.FactsheetActionBean; import eionet.cr.web.util.ApplicationCache; import eionet.cr.web.util.columns.SearchResultColumn; import eionet.cr.web.util.columns.SubjectPredicateColumn; /** * @author Aleksandr Ivanov * @author Enriko Ksper */ @UrlBinding("/typeSearch.action") public class TypeSearchActionBean extends AbstractSearchActionBean<SubjectDTO> { /** * Last action types on type search page. */ private enum LastAction { ADD_FILTER, REMOVE_FILTER, APPLY_FILTERS, SET_COLUMNS, SEARCH, SORTING; } private static final String AVAILABLE_COLUMNS_CACHE = TypeSearchActionBean.class.getName() + ".availableColumnsCache"; private static final String SELECTED_COLUMNS_CACHE = TypeSearchActionBean.class.getName() + ".selectedColumnsCache"; private static final String SELECTED_FILTERS_CACHE = TypeSearchActionBean.class.getName() + ".selectedFiltersCache"; private static final String RESULT_LIST_CACHED = TypeSearchActionBean.class.getName() + ".resultListCached"; private static final String LAST_ACTION = TypeSearchActionBean.class.getName() + ".lastAction"; private static final String MATCH_COUNT = TypeSearchActionBean.class.getName() + ".matchCount"; private static final String EXACT_COUNT = TypeSearchActionBean.class.getName() + ".exactCount"; /** * Default number of columns displayed on the search results. */ private static final int DEFAULT_NUMBER_OF_SELECTED_COLUMNS = 5; private static final String PREVIOUS_TYPE = TypeSearchActionBean.class.getName() + ".previousType"; /** * Type search jsp path. */ private static final String TYPE_SEARCH_PATH = "/pages/typeSearch.jsp"; /** * Type to search. */ private String type; /** * Selected columns. */ private List<String> selectedColumns; /** * Filter to add. */ private String newFilter; /** * Filter to be removed. */ private String clearFilter; /** * Selected filters. */ private Map<String, String> selectedFilters; /** * Selected filters with labels. */ private Map<String, Pair<String, String>> displayFilters; /** * show URI as resource identifier */ private boolean uriResourceIdentifier; /** * Export format selection. */ private String exportFormat; /** * Export columns. */ private List<String> exportColumns; /** * Query string. */ private String queryString; private boolean typesByName; /** * * @return Resolution. * @throws Exception */ @DefaultHandler public Resolution preparePage() throws Exception { Enumeration<String> names = getContext().getRequest().getParameterNames(); while (names.hasMoreElements()) { String next = names.nextElement(); if (next.startsWith("removeFilter_")) { clearFilter = next.replaceFirst("removeFilter_", ""); return removeFilter(); } } setExportColumns(getSelectedColumnsFromCache()); return new ForwardResolution(TYPE_SEARCH_PATH); } /** * @return a list of export formats. */ public ExportFormat[] getExportFormats() { return ExportFormat.values(); } /** * @return true if Excel export is available. */ public boolean isShowExcelExport() { if (matchCount > XlsExporter.getRowsLimit()) { return false; } return true; } /** * Exports search result as a file. * * @return Resolution. * @throws Exception */ @SuppressWarnings("unchecked") public Resolution export() throws Exception { logger.trace("************** START EXPORT REQUEST ***********"); restoreStateFromSession(); ExportFormat format = ExportFormat.fromName(exportFormat); Exporter exporter = Exporter.getExporter(format); exporter.setLanguages(getAcceptedLanguages() != null ? getAcceptedLanguages() : Collections.EMPTY_LIST); List<Pair<String, String>> columnPairs = new LinkedList<Pair<String, String>>(); exporter.setExportResourceUri(uriResourceIdentifier); selectedColumns = exportColumns == null || exportColumns.isEmpty() ? selectedColumns : exportColumns; for (String selectedColumn : selectedColumns) { columnPairs.add(new Pair<String, String>(selectedColumn, getAvailableColumns().get(selectedColumn))); } exporter.setSelectedColumns(columnPairs); Map<String, String> filters = new HashMap<String, String>(); if (selectedFilters != null) { filters.putAll(selectedFilters); } filters.put(Predicates.RDF_TYPE, type); exporter.setSelectedFilters(filters); getContext().getResponse().setHeader("Content-Disposition", "attachment;filename=" + format.getFilename()); getContext().getResponse().setHeader("Cache-Control", "no-cache, must-revalidate"); return new StreamingResolution(format.getContentType(), exporter.export()); } /** * @return */ public Resolution introspect() { if (!StringUtils.isBlank(type)) { return new RedirectResolution(FactsheetActionBean.class).addParameter("uri", type); } else { return new ForwardResolution(TYPE_SEARCH_PATH); } } /** * @return */ @SuppressWarnings("unchecked") public Resolution addFilter() { Map<String, Map<String, String>> cache = (Map<String, Map<String, String>>) getSession() .getAttribute(SELECTED_FILTERS_CACHE); if (cache == null) { cache = new HashMap<String, Map<String, String>>(); } if (!cache.containsKey(type)) { Map<String, String> filters = new LinkedHashMap<String, String>(); cache.put(type, filters); } cache.get(type).put(newFilter, null); getSession().setAttribute(SELECTED_FILTERS_CACHE, cache); getSession().setAttribute(LAST_ACTION, LastAction.ADD_FILTER); setExportColumns(getSelectedColumnsFromCache()); return new RedirectResolution(getUrlBinding() + "?search=Search&type=" + Util.urlEncode(type)); } /** * @return Resolution. */ @SuppressWarnings("unchecked") public Resolution removeFilter() { Map<String, Map<String, String>> cache = (Map<String, Map<String, String>>) getSession() .getAttribute(SELECTED_FILTERS_CACHE); if (cache != null && cache.containsKey(type) && cache.get(type) != null && cache.get(type).containsKey(clearFilter)) { cache.get(type).remove(clearFilter); } getSession().setAttribute(LAST_ACTION, LastAction.REMOVE_FILTER); setExportColumns(getSelectedColumnsFromCache()); return new RedirectResolution(getUrlBinding() + "?search=Search&type=" + Util.urlEncode(type)); } /** * @return Resolution. * @throws DAOException */ public Resolution applyFilters() throws DAOException { if (selectedFilters != null && !selectedFilters.isEmpty()) { // save selected filters for future use Map<String, Map<String, String>> cache = (Map<String, Map<String, String>>) getSession() .getAttribute(SELECTED_FILTERS_CACHE); if (cache != null && cache.containsKey(type)) { cache.get(type).putAll(selectedFilters); } } getSession().setAttribute(LAST_ACTION, LastAction.REMOVE_FILTER); setExportColumns(getSelectedColumnsFromCache()); return search(); } /* * (non-Javadoc) * * @see eionet.cr.web.action.AbstractSearchActionBean#search() */ @Override public Resolution search() throws DAOException { logger.trace("************** START SEARCH REQUEST ***********"); if (!StringUtils.isBlank(type)) { long startTime = System.currentTimeMillis(); restoreStateFromSession(); logger.trace("after restoreStateFromSession " + Util.durationSince(startTime)); startTime = System.currentTimeMillis(); LastAction lastAction = getLastAction(); if (resultList == null || !(lastAction != null && lastAction.equals(LastAction.ADD_FILTER))) { Map<String, String> criteria = new HashMap<String, String>(); criteria.put(Predicates.RDF_TYPE, type); if (selectedFilters != null && !selectedFilters.isEmpty()) { for (Entry<String, String> entry : selectedFilters.entrySet()) { if (!StringUtils.isBlank(entry.getValue())) { criteria.put(entry.getKey(), StringUtils.trim(entry.getValue().trim())); } } } SearchResultDTO<SubjectDTO> searchResult = DAOFactory.get().getDao(SearchDAO.class) .searchByTypeAndFilters(criteria, false, PagingRequest.create(getPageN()), new SortingRequest(getSortP(), SortOrder.parse(getSortO())), selectedColumns); resultList = searchResult.getItems(); matchCount = searchResult.getMatchCount(); queryString = searchResult.getQuery(); int exactRowCountLimit = DAOFactory.get().getDao(SearchDAO.class).getExactRowCountLimit(); exactCount = exactRowCountLimit <= 0 || matchCount <= exactRowCountLimit; } // cache result list. getSession().setAttribute(RESULT_LIST_CACHED, resultList); getSession().setAttribute(LAST_ACTION, LastAction.SEARCH); getSession().setAttribute(MATCH_COUNT, matchCount); getSession().setAttribute(EXACT_COUNT, exactCount); } setExportColumns(getSelectedColumnsFromCache()); return new ForwardResolution(TYPE_SEARCH_PATH); } /** * @return last action. */ private LastAction getLastAction() { if (getContext().getRequest().getParameter("pageN") != null) { return LastAction.SORTING; } else { return (LastAction) getSession().getAttribute(LAST_ACTION); } } /** * Restores actionBean state from session. */ private void restoreStateFromSession() throws DAOException { // restoring the result list resultList = (Collection<SubjectDTO>) getSession().getAttribute(RESULT_LIST_CACHED); Integer temp = (Integer) getSession().getAttribute(MATCH_COUNT); matchCount = temp == null ? 0 : temp.intValue(); // check if user has previously selected some columns. Map<String, List<String>> cache = (Map<String, List<String>>) getSession() .getAttribute(SELECTED_COLUMNS_CACHE); if (cache != null && cache.containsKey(type)) { selectedColumns = cache.get(type); } else { selectedColumns = new LinkedList<String>(); int i = 1; for (Entry<String, String> pair : getAvailableColumns().entrySet()) { selectedColumns.add(pair.getKey()); i++; if (i > DEFAULT_NUMBER_OF_SELECTED_COLUMNS) { break; } } } // saving to session selected type. String previousType = (String) getSession().getAttribute(PREVIOUS_TYPE); getSession().setAttribute(PREVIOUS_TYPE, type); if (!type.equals(previousType)) { Map<String, Map<String, String>> filterCache = (Map<String, Map<String, String>>) getSession() .getAttribute(SELECTED_FILTERS_CACHE); if (filterCache != null && filterCache.containsKey(type)) { filterCache.remove(type); } } else { // check for selected filters and add labels to them Map<String, Map<String, String>> filterCache = (Map<String, Map<String, String>>) getSession() .getAttribute(SELECTED_FILTERS_CACHE); if (filterCache != null && filterCache.containsKey(type)) { selectedFilters = filterCache.get(type); displayFilters = new HashMap<String, Pair<String, String>>(); Map<String, String> availableColumns = getAvailableColumns(); for (Entry<String, String> entry : selectedFilters.entrySet()) { if (availableColumns.containsKey(entry.getKey())) { displayFilters.put(availableColumns.get(entry.getKey()), new Pair<String, String>(entry.getKey(), entry.getValue())); } } } } } /** * * @return */ public Resolution setSearchColumns() { Map<String, List<String>> cache = (Map<String, List<String>>) getSession() .getAttribute(SELECTED_COLUMNS_CACHE); if (cache == null) { cache = new HashMap<String, List<String>>(); } if (selectedColumns != null) { // hardcode title into the selected columns if (!selectedColumns.contains(Predicates.RDFS_LABEL)) { selectedColumns.add(0, Predicates.RDFS_LABEL); } selectedColumns = new LinkedList<String>(selectedColumns.subList(0, selectedColumns.size())); } cache.put(type, selectedColumns); getSession().setAttribute(SELECTED_COLUMNS_CACHE, cache); getSession().setAttribute(LAST_ACTION, LastAction.SET_COLUMNS); return new RedirectResolution(getUrlBinding() + "?search=Search&type=" + Util.urlEncode(type)); } private List<String> getSelectedColumnsFromCache() { Map<String, List<String>> cache = (Map<String, List<String>>) getSession() .getAttribute(SELECTED_COLUMNS_CACHE); return cache == null ? selectedColumns : cache.get(type); } /* * (non-Javadoc) * * @see eionet.cr.web.action.AbstractSearchActionBean#getColumns() */ @Override public List<SearchResultColumn> getColumns() throws DAOException { List<SearchResultColumn> columns = new LinkedList<SearchResultColumn>(); // setSelectedColumns uses POST to submit a form, we have to add needed parameters to the // column headers in order to be able to sort right. String actionParameter = "search=Search&type=" + eionet.cr.util.Util.urlEncode(type); // add every selected column to the output if (selectedColumns != null && !selectedColumns.isEmpty()) { for (String string : selectedColumns) { for (Entry<String, String> pair : getAvailableColumns().entrySet()) { if (pair.getKey().equals(string)) { SubjectPredicateColumn column = new SubjectPredicateColumn(pair.getValue(), true, string, actionParameter); column.setSubjectTypes(type); columns.add(column); } } } } return columns; } /** * * @return */ public List<Pair<String, String>> getAvailableTypesNoGroup() { return SortStringPair.sortByLeftAsc(ApplicationCache.getTypes()); } /** * * @return */ public List<Pair<String, List<Pair<String, String>>>> getAvailableTypes() { List<Pair<String, String>> sourceTypes = ApplicationCache.getTypes(); /** * Defining the returnTypes so that it contains group name as the outer Left and contents as outer Right Those groups form a * list that contains Pair's, each of them containing Pairs of types. */ List<Pair<String, List<Pair<String, String>>>> returnTypes = new ArrayList<Pair<String, List<Pair<String, String>>>>(); String previousPrefix = ""; int i = 0; List<Pair<String, String>> groupedTypes; SortStringPair.sortByLeftAsc(sourceTypes); boolean itemsAdded = false; groupedTypes = new ArrayList<Pair<String, String>>(); while (i < sourceTypes.size()) { String currentPrefix = ""; // Disassembling the prefix if (sourceTypes.get(i).getLeft().replaceAll("[^#]", "").length() > 0) { // Meaning that the url contains #-char currentPrefix = sourceTypes.get(i).getLeft().split("#")[0]; } else { // Must be split by last '/' String[] tokens = sourceTypes.get(i).getLeft().split("/"); for (int j = 0; j < tokens.length - 1; j++) { currentPrefix += tokens[j] + "/"; } } if (!currentPrefix.equals(previousPrefix)) { // Adding previous group to return output, initializing new groupedTypes. if (itemsAdded) { returnTypes.add(new Pair<String, List<Pair<String, String>>>(previousPrefix, groupedTypes)); itemsAdded = false; } groupedTypes = new ArrayList<Pair<String, String>>(); } if (groupedTypes != null) { groupedTypes.add(sourceTypes.get(i)); itemsAdded = true; } // Incrementing loop counter. i++; // The loop is over, last group shall be added manually in case there are items (itemsAdded == true). if (i == sourceTypes.size()) { if (itemsAdded) { returnTypes.add(new Pair<String, List<Pair<String, String>>>(previousPrefix, groupedTypes)); } } previousPrefix = currentPrefix; } return returnTypes; } /** * * @return */ public List<Pair<String, String>> getAvailableTypesByName() { List<Pair<String, String>> sourceTypes = ApplicationCache.getTypes(); SortStringPair.sortByRightAsc(sourceTypes); int i = 0; List<Pair<String, String>> result = new ArrayList<Pair<String, String>>(); while (i < sourceTypes.size()) { String currentPrefix = ""; // Disassembling the prefix if (sourceTypes.get(i).getLeft().replaceAll("[^#]", "").length() > 0) { // Meaning that the url contains #-char currentPrefix = sourceTypes.get(i).getLeft().split("#")[0]; } else { // Must be split by last '/' String[] tokens = sourceTypes.get(i).getLeft().split("/"); for (int j = 0; j < tokens.length - 1; j++) { currentPrefix += tokens[j] + "/"; } } String nameWithUri = sourceTypes.get(i).getRight() + " (" + currentPrefix + ")"; String uri = sourceTypes.get(i).getLeft(); result.add(new Pair<String, String>(uri, nameWithUri)); // Incrementing loop counter. i++; } return result; } /** * @return the type */ public String getType() { return type; } /** * @param type the type to set */ public void setType(String type) { this.type = type; } /** * @return the selectedColumns */ public List<String> getSelectedColumns() { return selectedColumns; } /** * @param selectedColumns the selectedColumns to set */ public void setSelectedColumns(List<String> selectedColumns) { this.selectedColumns = selectedColumns; } /** * * @return * @throws DAOException */ public Map<String, String> getAvailableColumnsSorted() throws DAOException { return sortByValueIgnoreCase(getAvailableColumns()); } /** * @return the availableColumns */ @SuppressWarnings("unchecked") public Map<String, String> getAvailableColumns() throws DAOException { Map<String, Map<String, String>> cache = (Map<String, Map<String, String>>) getSession() .getAttribute(AVAILABLE_COLUMNS_CACHE); if (cache == null) { cache = new HashMap<String, Map<String, String>>(); } if (!cache.containsKey(type)) { Map<String, String> result = new LinkedHashMap<String, String>(); List<Pair<String, String>> usedPredicates = DAOFactory.get().getDao(HelperDAO.class) .getPredicatesUsedForType(type); result.put(Predicates.RDFS_LABEL, "Title"); if (usedPredicates != null && !usedPredicates.isEmpty()) { for (Pair<String, String> pair : usedPredicates) { if (pair != null) { String uri = pair.getLeft(); String label = pair.getRight(); if (StringUtils.isBlank(label) || label.equalsIgnoreCase("null")) { label = URIUtil.extractURILabel(uri, uri); } if (!StringUtils.isBlank(label)) { result.put(uri, label); } } } } // hardcode RDF_TYPE as one of the available values result.put(Predicates.RDF_TYPE, "type"); cache.put(type, result); getSession().setAttribute(AVAILABLE_COLUMNS_CACHE, cache); return result; } else { return cache.get(type); } } /** * @return available filters. * @throws DAOException */ public Map<String, String> getAvailableFilters() throws DAOException { Map<String, String> result = new HashMap<String, String>(); result.putAll(getAvailableColumns()); result.remove(Predicates.RDF_TYPE); if (selectedFilters != null) { for (String key : selectedFilters.keySet()) { result.remove(key); } } return result; } /** * * @return * @throws DAOException */ public Map<String, String> getAvailableFiltersSorted() throws DAOException { return sortByValueIgnoreCase(getAvailableFilters()); } /** * @return the selectedFilter */ public String getNewFilter() { return newFilter; } /** * @param selectedFilter the selectedFilter to set */ public void setNewFilter(String selectedFilter) { this.newFilter = selectedFilter; } /** * @return the selectedFilters */ public Map<String, String> getSelectedFilters() { return selectedFilters; } /** * @param selectedFilters the selectedFilters to set */ public void setSelectedFilters(Map<String, String> selectedFilters) { this.selectedFilters = selectedFilters; } /** * @return the removeFilter */ public String getClearFilter() { return clearFilter; } /** * @param removeFilter the removeFilter to set */ public void setClearFilter(String removeFilter) { this.clearFilter = removeFilter; } /** * @return the displayFilters */ public Map<String, Pair<String, String>> getDisplayFilters() { return displayFilters; } /** * @return the uriResourceIdentifier */ public boolean isUriResourceIdentifier() { return uriResourceIdentifier; } /** * @param uriResourceIdentifier the uriResourceIdentifier to set */ public void setUriResourceIdentifier(boolean uriResourceIdentifier) { this.uriResourceIdentifier = uriResourceIdentifier; } /** * @return the exportColumns */ public List<String> getExportColumns() { return exportColumns; } /** * @return the exportFormat */ public String getExportFormat() { return exportFormat; } /** * set exportFormat parameter (xls, xml, ...) * * @param exportFormat */ public void setExportFormat(String exportFormat) { this.exportFormat = exportFormat; } /** * @param exportColumns the exportColumns to set */ public void setExportColumns(List<String> exportColumns) { this.exportColumns = exportColumns; } public String getQueryString() { return queryString; } /** * * @param map * @return */ private Map<String, String> sortByValueIgnoreCase(Map<String, String> map) { LinkedHashMap<String, String> result = new LinkedHashMap<String, String>(); if (map != null && !map.isEmpty()) { List<String> keys = new ArrayList<String>(map.keySet()); List<String> values = new ArrayList<String>(map.values()); List<String> sortedValues = new ArrayList<String>(map.values()); Collections.sort(sortedValues, new CaseInsensitiveStringComparator()); for (int i = 0; i < sortedValues.size(); i++) { result.put(keys.get(values.indexOf(sortedValues.get(i))), sortedValues.get(i)); } } return result; } public boolean isTypesByName() { return typesByName; } public void setTypesByName(boolean typesByName) { this.typesByName = typesByName; } /** * Case-insensitive string comparator that handles null arguments. * * @author <a href="mailto:jaanus.heinlaid@tietoenator.com">Jaanus Heinlaid</a> * */ private class CaseInsensitiveStringComparator implements Comparator<String> { /* * (non-Javadoc) * * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ @Override public int compare(String str1, String str2) { if (str1 == null && str2 == null) { return 0; } else if (str1 == null && str2 != null) { return -1; } else if (str1 != null && str2 == null) { return 1; } else { return (str1).compareToIgnoreCase(str2); } } } }