/******************************************************************************* * * Copyright 2010 Alexandru Craciun, and individual contributors as indicated * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ******************************************************************************/ package org.netxilia.server.rest; import java.security.AccessControlException; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.TreeSet; import javax.swing.tree.DefaultMutableTreeNode; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.netxilia.api.exception.NetxiliaBusinessException; import org.netxilia.api.exception.NetxiliaResourceException; import org.netxilia.api.exception.NotFoundException; import org.netxilia.api.exception.StorageException; import org.netxilia.api.model.CellData; import org.netxilia.api.model.ISheet; import org.netxilia.api.model.IWorkbook; import org.netxilia.api.model.SheetFullName; import org.netxilia.api.model.SheetType; import org.netxilia.api.model.WorkbookId; import org.netxilia.api.reference.AreaReference; import org.netxilia.api.storage.DataSourceConfiguration; import org.netxilia.api.storage.DataSourceConfigurationId; import org.netxilia.api.storage.IDataSourceConfigurationService; import org.netxilia.api.user.IAclService; import org.netxilia.api.user.IUserService; import org.netxilia.api.user.Permission; import org.netxilia.api.user.User; import org.netxilia.api.utils.Matrix; import org.netxilia.api.utils.Pair; import org.netxilia.jaxrs.html.ModelAndView; import org.netxilia.server.rest.html.HomeModel; import org.netxilia.server.util.StringHolder; import org.springframework.beans.factory.annotation.Autowired; @Path("/home") public class HomeResource extends AbstractResource { private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(HomeResource.class); private static final String FOLDERS_SHEET = "folders"; @Autowired private IDataSourceConfigurationService dataSourceConfigurationService; @Autowired private IAclService aclService; @Autowired private IUserService userService; public IDataSourceConfigurationService getDataSourceConfigurationService() { return dataSourceConfigurationService; } public void setDataSourceConfigurationService(IDataSourceConfigurationService dataSourceConfigurationService) { this.dataSourceConfigurationService = dataSourceConfigurationService; } public IAclService getAclService() { return aclService; } public void setAclService(IAclService aclService) { this.aclService = aclService; } public IUserService getUserService() { return userService; } public void setUserService(IUserService userService) { this.userService = userService; } private DataSourceConfigurationId getMostUsedDataSource() throws StorageException { Map<DataSourceConfigurationId, Integer> dataSourceWorkbookCount = new HashMap<DataSourceConfigurationId, Integer>(); List<Pair<WorkbookId, DataSourceConfigurationId>> workbooksAndConfigs = dataSourceConfigurationService .findAllWorkbooksConfigurations(); for (Pair<WorkbookId, DataSourceConfigurationId> wkCfg : workbooksAndConfigs) { Integer count = dataSourceWorkbookCount.get(wkCfg.getSecond()); dataSourceWorkbookCount.put(wkCfg.getSecond(), count != null ? count + 1 : 0); } DataSourceConfigurationId maxEntry = null; int max = Integer.MIN_VALUE; for (Map.Entry<DataSourceConfigurationId, Integer> entry : dataSourceWorkbookCount.entrySet()) { if (entry.getValue() > max) { maxEntry = entry.getKey(); max = entry.getValue(); } } return maxEntry; } @GET @Produces(MediaType.TEXT_HTML) public ModelAndView<HomeModel> get() throws NetxiliaResourceException, NetxiliaBusinessException { HomeModel homeModel = new HomeModel(treeview().getValue(), getMostUsedDataSource()); return new ModelAndView<HomeModel>(homeModel, "/WEB-INF/jsp/home.jsp"); } @GET @Path("/treeview") @Produces(MediaType.APPLICATION_JSON) public StringHolder treeview() throws NetxiliaResourceException, NetxiliaBusinessException { // TODO - here a light version if IWorkbook should be return to not be able to get around security exceptions // add workbooks List<Pair<WorkbookId, DataSourceConfigurationId>> workbooksAndConfigs = dataSourceConfigurationService .findAllWorkbooksConfigurations(); DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(null); for (Pair<WorkbookId, DataSourceConfigurationId> wkCfg : workbooksAndConfigs) { WorkbookId wkId = wkCfg.getFirst(); IWorkbook workbook = null; Set<SheetFullName> sheetNames = new TreeSet<SheetFullName>(); try { workbook = getWorkbookProcessor().getWorkbook(wkId); for (ISheet sheet : workbook.getSheets()) { if (sheet.getType() == SheetType.normal) { try { aclService.checkPermission(sheet.getFullName(), Permission.read); } catch (AccessControlException e) { continue; } sheetNames.add(sheet.getFullName()); } } } catch (AccessControlException ex) { // user has not write to see the workbook continue; } catch (Exception ex) { log.error("Could not load workbook " + wkId + ":" + ex, ex); // add the workbook with error DefaultMutableTreeNode workbookNode = new DefaultMutableTreeNode(new TreeViewData(wkId.getKey(), wkId.getKey(), "workbook error")); rootNode.add(workbookNode); continue; } ISheet foldersSheet = null; SheetFullName folderSheetName = new SheetFullName(workbook.getName(), FOLDERS_SHEET); try { foldersSheet = getWorkbookProcessor().getWorkbook(wkId).getSheet(FOLDERS_SHEET); } catch (NotFoundException ex) { // no folder sheet } catch (Exception ex) { log.error("Could not load folder sheet" + folderSheetName + ":" + ex, ex); } DefaultMutableTreeNode workbookNode = buildWorkbookTree(workbook, foldersSheet, sheetNames); rootNode.add(workbookNode); } // only for admins User currentUser = userService.getCurrentUser(); if (currentUser != null && currentUser.isAdmin()) { // add admin DefaultMutableTreeNode adminNode = new DefaultMutableTreeNode(new TreeViewData("admin", "Administration", "admin", true)); rootNode.add(adminNode); // add datasources nodes DefaultMutableTreeNode dsNode = new DefaultMutableTreeNode(new TreeViewData("ds", "Datasources", "datasources", true)); adminNode.add(dsNode); for (DataSourceConfiguration dsConfig : dataSourceConfigurationService.findAll()) { dsNode.add(new DefaultMutableTreeNode(new TreeViewData(dsConfig.getId().toString(), dsConfig.getName(), "datasource"))); } // add modules nodes DefaultMutableTreeNode moduleNode = new DefaultMutableTreeNode(new TreeViewData("modules", "Modules", "modules", true)); adminNode.add(moduleNode); // add build nodes DefaultMutableTreeNode buildNode = new DefaultMutableTreeNode(new TreeViewData("build", "Custom Modules", "build", true)); adminNode.add(buildNode); } StringBuilder treeview = new StringBuilder(); buildTreeView(rootNode, treeview); return new StringHolder(treeview.toString()); } /** * walk the tree and dump the node to the tree * * @param node * @param treeview */ @SuppressWarnings("unchecked") private void buildTreeView(DefaultMutableTreeNode node, StringBuilder treeview) { // <ul id="example" class="filetree"> // <li><span class="folder">Folder 1</span> // <ul> // <li><span class="file">Item 1.1</span></li> // </ul> // </li> // <li><span class="folder">Folder 2</span> TreeViewData viewData = (TreeViewData) node.getUserObject(); if (node.isLeaf()) { if (viewData != null) { treeview.append("<li><span id='").append(viewData.getId()).append("' class='") .append(viewData.getCssClass()).append("'>").append(viewData.getName()).append("</span></li>"); } } else { if (viewData != null) { if (viewData.isCollapsed()) { treeview.append("<li class='closed'>"); } else { treeview.append("<li>"); } treeview.append("<span class='folder ").append(viewData.getCssClass()).append("'>") .append(viewData.getName()).append("</span><ul>"); } Enumeration<DefaultMutableTreeNode> childrenEnum = node.children(); while (childrenEnum.hasMoreElements()) { buildTreeView(childrenEnum.nextElement(), treeview); } if (viewData != null) { treeview.append("</ul></li>"); } } } /** * build the HTML tags for a tree like view * * @param foldersSheet * @param treeview * @throws NetxiliaBusinessException * @throws NetxiliaResourceException */ private DefaultMutableTreeNode buildWorkbookTree(IWorkbook workbook, ISheet foldersSheet, Set<SheetFullName> sheetNames) throws NetxiliaResourceException, NetxiliaBusinessException { DefaultMutableTreeNode workbookNode = new DefaultMutableTreeNode(new TreeViewData(workbook.getId().getKey(), workbook.getName(), "workbook")); Stack<DefaultMutableTreeNode> stockNodes = new Stack<DefaultMutableTreeNode>(); stockNodes.push(workbookNode); Set<SheetFullName> alreadyInsertedSheets = new HashSet<SheetFullName>(); if (foldersSheet != null) { Matrix<CellData> folderCells = foldersSheet.receiveCells(AreaReference.ALL).getNonBlocking(); for (List<CellData> row : folderCells.getRows()) { int level = 0; String nodeName = null; for (CellData cell : row) { if (cell.getValue() != null) { nodeName = cell.getValue().getStringValue(); if (nodeName != null && nodeName.length() > 0) { level = cell.getReference().getColumnIndex(); break; } } } if (nodeName == null) { // empty line - ignored continue; } // first level for folders is 1 (under the root node) level = level + 1; SheetFullName sheetName = new SheetFullName(workbook.getName(), nodeName); boolean isSheet = sheetNames.contains(sheetName); if (isSheet) { alreadyInsertedSheets.add(sheetName); } DefaultMutableTreeNode crt = new DefaultMutableTreeNode(new TreeViewData(sheetName.toString(), sheetName.getSheetName(), isSheet ? "sheet" : "folder")); while (!stockNodes.empty()) { DefaultMutableTreeNode node = stockNodes.peek(); if (level > node.getLevel()) { // make sure is the direct child node.add(crt); break; } stockNodes.pop(); } stockNodes.push(crt); } } // add the sheets not already added for (SheetFullName sheetName : sheetNames) { if (alreadyInsertedSheets.contains(sheetName)) { continue; } DefaultMutableTreeNode sheetNode = new DefaultMutableTreeNode(new TreeViewData(sheetName.toString(), sheetName.getSheetName(), "sheet")); workbookNode.add(sheetNode); } return workbookNode; } private static class TreeViewData { private final String id; private final String name; private final String cssClass; private final boolean collapsed; public TreeViewData(String id, String name, String cssClass) { this(id, name, cssClass, false); } public TreeViewData(String id, String name, String cssClass, boolean collapsed) { this.id = id; this.name = name; this.cssClass = cssClass; this.collapsed = collapsed; } public String getId() { return id; } public String getName() { return name; } public String getCssClass() { return cssClass; } public boolean isCollapsed() { return collapsed; } } }