/* * Copyright (c) 2006-2013 by Public Library of Science http://plos.org http://ambraproject.org * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ambraproject.action.taxonomy; import com.opensymphony.xwork2.ActionContext; import org.ambraproject.ApplicationException; import org.ambraproject.action.BaseActionSupport; import org.ambraproject.service.taxonomy.TaxonomyService; import org.ambraproject.util.CategoryUtils; import org.ambraproject.views.CategoryView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import java.util.List; import java.util.Map; import java.util.SortedSet; /** * Action class for displaying a list of all top-level and second-level categories * in the taxonomy associated with any article. This also generates JSON responses for the taxonony browser * * @author John Callaway * @author Joe Osowski */ public class TaxonomyAction extends BaseActionSupport { private static final Logger log = LoggerFactory.getLogger(TaxonomyAction.class); private Map<String, List<String>> topAndSecondLevelCategories; private CategoryView categoryView; private TaxonomyService taxonomyService; private String root; private String journal; private String[] filter; private Map<String, SortedSet<String>> categories; private boolean showCounts; private Map<String, Long> counts; @Override public String execute() throws Exception { String browseValueKey = "ambra.virtualJournals." + getCurrentJournal() + ".taxonomyBrowser"; //This journal not configured to use the taxonomy browser, return 404 //This action is also called via a JSON request to render taxonomy options //on the userProfile page. In this case the context name is taxonomyJSON, and we don't want //to return INPUT if(!configuration.getBoolean(browseValueKey, false) && ActionContext.getContext().getName().equals("taxonomy") ) { return INPUT; } //topAndSecondLevelCategories defaults to current journal topAndSecondLevelCategories = taxonomyService.parseTopAndSecondLevelCategories(getCurrentJournal()); //categories defaults to all journals (the categories journal can be set via parameter) categoryView = taxonomyService.parseCategories(this.journal); buildCategoryMap(); return SUCCESS; } /** * @return a map from top-level category to list of second-level subcategories * associated with that top-level category */ public Map<String, List<String>> getTopAndSecondLevelCategories() { return topAndSecondLevelCategories; } @Required public void setTaxonomyService(TaxonomyService taxonomyService) { this.taxonomyService = taxonomyService; } /** * Returns a set of categories and the count of children * * Note: This is will return a subset of categories post filter applied. * * If there is no filter, the set returned will be the root nodes * If "/Biotech" is set as the filter, the set returned will have "/BioTech" as the root node. * If "/Biotech/Genetics" is set as the filter, the set returned will have "/BioTech/Genetics" as the root node. * * @return A map of categories */ @SuppressWarnings("unchecked") private void buildCategoryMap() throws ApplicationException { //Should probably implement this in the setter if the getter ever starts to get //called more then once if(this.root == null) { categories = CategoryUtils.getShortTree(categoryView); root = ""; } //Ignore first slash if it exists if(this.root.trim().length() > 0 && this.root.trim().charAt(0) == '/') { this.root = this.root.trim().substring(1); } if(this.root.trim().length() == 0) { categories = CategoryUtils.getShortTree(categoryView); } else { String[] levels = this.root.split("/"); for(String level : levels) { categoryView = categoryView.getChild(level); } categories = CategoryUtils.getShortTree(categoryView); } if (showCounts) { counts = taxonomyService.getCounts(categoryView, journal); } } /** * We never want to return the entire map as it is too large. However, * if filters are defined, we can return a subset * * @return */ public CategoryView getMap() { //Should probably implement this in the setter if the getter ever starts to get //called more then once if(this.filter != null && this.filter.length > 0) { for(String filter : this.filter) { if(filter.length() < 3) { addActionError("All filters defined must be at least 3 characters long"); } } if(getActionErrors().size() == 0) { CategoryView cv = CategoryUtils.filterMap(categoryView, this.filter); return cv; } } return null; } /** * Set the root for what categories are to be returned */ public void setFilter(String[] filter) { this.filter = filter; } /** * Set the root for what categories are to be returned */ public void setRoot(String root) { this.root = root; } /** * Set the journal for what categories are to be returned */ public void setJournal(String journal) { this.journal = journal; } public Map<String, SortedSet<String>> getCategories() { return categories; } /** * @param showCounts if true, information about the number of articles associated with each taxonomy * term will be returned in the response */ public void setShowCounts(boolean showCounts) { this.showCounts = showCounts; } public Map<String, Long> getCounts() { return counts; } }