/******************************************************************************* * Copyright 2012 University of Southern California * * 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.0 * * Unless 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. * * This code was developed by the Information Integration Group as part * of the Karma project at the Information Sciences Institute of the * University of Southern California. For more information, publications, * and related projects, please see: http://www.isi.edu/integration ******************************************************************************/ package edu.isi.karma.view; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.isi.karma.controller.update.WorksheetListUpdate; import edu.isi.karma.rep.HNode; import edu.isi.karma.rep.HNodePath; import edu.isi.karma.rep.HTable; import edu.isi.karma.rep.Table; import edu.isi.karma.rep.TablePager; import edu.isi.karma.rep.Worksheet; import edu.isi.karma.util.JSONUtil; import edu.isi.karma.view.ViewPreferences.ViewPreference; public class VWorksheet extends ViewEntity { private final Worksheet worksheet; /** * Marks whether the data in the view is consistent with the data in the * memory representation. When false, it means that the view should be * refreshed, and an indication should be shown to the user to indicate that * an explicit refresh is needed. */ private boolean upToDate = true; /** * When true, the view should show the worksheet collapsed so that the * headers are visible but the data is hidden. */ private boolean collapsed = false; /** * The column headers shown in this view. The hidden columns do not appear * in this list. A roundtrip to the server is required to make hidden * columns appear. */ //private final List<HNodePath> columns; private ArrayList<VHNode> headerViewNodes; /** * The maximum number of rows to show in the nested tables. */ private int maxRowsToShowInNestedTables; /** * We create a TablePager for the top level table and every nested table we * see. It records how the table is scrolled. */ private Map<String, TablePager> tableId2TablePager = new HashMap<>(); private static Logger logger = LoggerFactory .getLogger(VWorksheet.class); VWorksheet(String id, Worksheet worksheet, List<HNodePath> columns, VWorkspace vWorkspace) { super(id); this.worksheet = worksheet; //this.columns = columns; this.maxRowsToShowInNestedTables = vWorkspace.getPreferences() .getIntViewPreferenceValue( ViewPreference.maxRowsToShowInNestedTables); // Force creation of the TablePager for the top table. getTablePager(worksheet.getDataTable(), vWorkspace.getPreferences().getIntViewPreferenceValue( ViewPreference.defaultRowsToShowInTopTables)); this.headerViewNodes = initHeaderViewNodes(worksheet.getHeaders()); } private ArrayList<VHNode> initHeaderViewNodes(HTable table) { ArrayList<VHNode> vNodes = new ArrayList<>(); for(String hNodeId : table.getOrderedNodeIds()) { HNode node = table.getHNode(hNodeId); VHNode vNode = new VHNode(node.getId(), node.getColumnName()); if(node.hasNestedTable()) { HTable nestedTable = node.getNestedTable(); ArrayList<VHNode> nestedNodes = initHeaderViewNodes(nestedTable); for(VHNode nestedVNode : nestedNodes) { vNode.addNestedNode(nestedVNode); } } vNodes.add(vNode); } return vNodes; } private TablePager getTablePager(Table table, int size) { TablePager tp = tableId2TablePager.get(table.getId()); if (tp != null) { return tp; } else { tp = new TablePager(table, size); tableId2TablePager.put(table.getId(), tp); return tp; } } public TablePager getTopTablePager() { return tableId2TablePager.get(worksheet.getDataTable().getId()); } public TablePager getNestedTablePager(Table table) { return getTablePager(table, maxRowsToShowInNestedTables); } public TablePager getTablePager(String tableId) { return tableId2TablePager.get(tableId); } public Map<String, TablePager> getTableId2TablePager() { return tableId2TablePager; } public void setTableId2TablePager(Map<String, TablePager> tableId2TablePager) { this.tableId2TablePager = tableId2TablePager; } public String getWorksheetId() { return worksheet.getId(); } public Worksheet getWorksheet() { return worksheet; } public boolean isUpToDate() { return upToDate; } public void setUpToDate(boolean upToDate) { this.upToDate = upToDate; } public boolean isCollapsed() { return collapsed; } public void setCollapsed(boolean collapsed) { this.collapsed = collapsed; } public int getMaxRowsToShowInNestedTables() { return maxRowsToShowInNestedTables; } public void setMaxRowsToShowInNestedTables(int maxRowsToShowInNestedTables) { this.maxRowsToShowInNestedTables = maxRowsToShowInNestedTables; } public ArrayList<VHNode> getHeaderViewNodes() { return this.headerViewNodes; } public JSONArray getHeaderViewNodesJSON() { JSONArray newColumns = new JSONArray(); for(VHNode node : this.headerViewNodes) { newColumns.put(getJSONRep(node)); } return newColumns; } private JSONObject getJSONRep(VHNode node) { JSONObject obj = new JSONObject(); obj.put("id", node.getId()); obj.put("name", node.getColumnName()); obj.put("visible", node.isVisible()); if(node.hasNestedTable()) { JSONArray children = new JSONArray(); for(VHNode childNode : node.getNestedNodes()) { children.put(getJSONRep(childNode)); } obj.put("children", children); } return obj; } public ArrayList<String> getHeaderVisibleNodes() { return getVisibleViewNodes(this.headerViewNodes); } private ArrayList<String> getVisibleViewNodes(ArrayList<VHNode> list) { ArrayList<String> visibleNodeIds = new ArrayList<>(); for(VHNode node : list) { if(node.isVisible()) { visibleNodeIds.add(node.getId()); visibleNodeIds.addAll(getVisibleViewNodes(node.getNestedNodes())); } } return visibleNodeIds; } public ArrayList<String> getHeaderVisibleLeafNodes() { return getHeaderVisibleLeafNodes(this.headerViewNodes); } private ArrayList<String> getHeaderVisibleLeafNodes(ArrayList<VHNode> list) { ArrayList<String> visibleNodeIds = new ArrayList<>(); for(VHNode node : list) { if(node.isVisible()) { if(node.hasNestedTable()) { visibleNodeIds.addAll(getHeaderVisibleLeafNodes(node.getNestedNodes())); } else { visibleNodeIds.add(node.getId()); } } } return visibleNodeIds; } // public ArrayList<VHNode> getHeaderViewNodes(String id) { // if(id.equals(worksheet.getHeaders().getId())) // return this.headerViewNodes; // // return getChildHeaderNodes(this.headerViewNodes, id); // } // // private ArrayList<VHNode> getChildHeaderNodes(ArrayList<VHNode> vNodes, String id) { // ArrayList<VHNode> children = null; // for(VHNode node : vNodes) { // if(node.getId().equals(id)) { // children = node.getNestedNodes(); // break; // } // // if(node.hasNestedTable()) { // ArrayList<VHNode> matchingChild = getChildHeaderNodes(node.getNestedNodes(), id); // if(matchingChild != null) { // children = matchingChild; // break; // } // } // } // return children; // } // public boolean isHeaderNodeVisible(String hnodeId) { return getHeaderVisibleNodes().contains(hnodeId); } private ArrayList<VHNode> generateOrganizedColumns(JSONArray columns) { ArrayList<VHNode> vNodes = new ArrayList<>(); for(int i=0; i<columns.length(); i++) { JSONObject column = columns.getJSONObject(i); if(column.has("id") && column.get("id") instanceof String) { try { VHNode vNode = new VHNode(column.getString("id"), column.get("name").toString()); vNode.setVisible(column.getBoolean("visible")); if(column.has("children")) { ArrayList<VHNode> nestedNodes = generateOrganizedColumns(column.getJSONArray("children")); for(VHNode nestedVNode : nestedNodes) { vNode.addNestedNode(nestedVNode); } } vNodes.add(vNode); } catch(Exception e) { logger.error("Error organizing column:" + column.toString(), e); } } } return vNodes; } public void organizeColumns(JSONArray columns) { this.headerViewNodes = generateOrganizedColumns(columns); } public void organizeColumns(ArrayList<VHNode> columns) { this.headerViewNodes.clear(); this.headerViewNodes.addAll(columns); } public void updateHeaderViewNodes(ArrayList<VHNode> oldOrderedNodes) { ArrayList<String> oldPaths = new ArrayList<>(); for(VHNode node : oldOrderedNodes) oldPaths.addAll(node.getAllPaths()); ArrayList<String> newPaths = new ArrayList<>(); for(VHNode node : this.headerViewNodes) newPaths.addAll(node.getAllPaths()); //1/. Get all paths in old that are not in new ArrayList<String> pathsToDel = new ArrayList<>(); for(String oldPath : oldPaths) { if(!newPaths.contains(oldPath)) { pathsToDel.add(oldPath); } } //2. Get all paths in new that are not in old ArrayList<String> pathsToAdd = new ArrayList<>(); for(int i=0; i<newPaths.size(); i++) { String newPath = newPaths.get(i); if(!oldPaths.contains(newPath)) { if (i != 0) { String after = newPaths.get(i-1); int difference = StringUtils.countMatches(after, "/") - StringUtils.countMatches(newPath, "/"); for (int k = 0; k < difference; k++) { after = after.substring(0, after.lastIndexOf("/")); } pathsToAdd.add(newPath + "$$" + after); } else { pathsToAdd.add(newPath + "$$" + "null"); } } } ArrayList<VHNode> allNodes = new ArrayList<>(); allNodes.addAll(oldOrderedNodes); this.headerViewNodes = allNodes; for(String path : pathsToDel) { deleteHeaderViewPath(this.headerViewNodes, path); } for(String path : pathsToAdd) { addHeaderViewPath(path); } } private void addHeaderViewPath(String path) { int after = path.indexOf("$$"); String afterPath = path.substring(after + 2); path = path.substring(0, after); ArrayList<VHNode> parentList = this.headerViewNodes; int idx; String pathStart = path; if((idx = pathStart.lastIndexOf("/")) != -1) { String parentSig = path.substring(0, idx); pathStart = path.substring(idx+1); VHNode parentNode = VHNode.getVHNodeFromPath(parentSig, this.headerViewNodes); parentList = parentNode.getNestedNodes(); } idx = pathStart.indexOf(":"); String id = pathStart.substring(0, idx); String name = pathStart.substring(idx+1); VHNode newNode = new VHNode(id, name); int insertIdx = -1; idx = afterPath.lastIndexOf("/"); if(idx != -1) afterPath = afterPath.substring(idx + 1); for(int i=0; i<parentList.size(); i++) { VHNode node = parentList.get(i); if(node.getNodePathSignature().equals(afterPath)) { insertIdx = i; break; } } parentList.add(insertIdx+1, newNode); } private void deleteHeaderViewPath(ArrayList<VHNode> nodes, String path) { int idx = path.indexOf("/"); String pathStart = path, pathEnd = null; if(idx != -1) { pathStart = path.substring(0, idx); pathEnd = path.substring(idx+1); } for(VHNode node : nodes) { if(node.getNodePathSignature().equals(pathStart)) { if(pathEnd == null) { nodes.remove(node); } else { deleteHeaderViewPath(node.getNestedNodes(), pathEnd); } break; } } } public void generateWorksheetListJson(String prefix, PrintWriter pw) { pw.println(prefix + "{"); String newPref = prefix + " "; pw.println(newPref + JSONUtil.json(WorksheetListUpdate.JsonKeys.worksheetId, this.getWorksheetId())); pw.println(newPref + JSONUtil.json(WorksheetListUpdate.JsonKeys.isUpToDate, upToDate)); pw.println(newPref + JSONUtil.json(WorksheetListUpdate.JsonKeys.isCollapsed, collapsed)); pw.println(newPref + JSONUtil.json(WorksheetListUpdate.JsonKeys.encoding, worksheet.getEncoding())); pw.println(newPref + JSONUtil.jsonLast(WorksheetListUpdate.JsonKeys.title, worksheet.getTitle())); pw.println(prefix + "}"); } }