--- /dev/null
+/*******************************************************************************\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.spreadsheet.ui;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+\r
+import java.awt.Rectangle;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+\r
+import org.simantics.spreadsheet.ClientModel;\r
+import org.simantics.spreadsheet.util.SpreadsheetUtils;\r
+import org.simantics.utils.datastructures.Pair;\r
+import org.simantics.utils.datastructures.collections.CollectionUtils;\r
+\r
+public class ClientModelImpl implements ClientModel {\r
+ \r
+ final private Map<String, Map<String, Object>> properties = new THashMap<String, Map<String, Object>>();\r
+ final private Map<String, Map<String, Object>> cells = new THashMap<String, Map<String, Object>>();\r
+ \r
+ private int maxRow = 0;\r
+ private int maxColumn = 0;\r
+ \r
+ private Set<String> clears = new HashSet<String>();\r
+// private ArrayList<Triple<String, String, Object>> modifications = new ArrayList<Triple<String, String, Object>>();\r
+ \r
+ private ArrayList<String> modLocation = new ArrayList<String>();\r
+ private ArrayList<String> modProperty = new ArrayList<String>();\r
+ private ArrayList<Object> modValue = new ArrayList<Object>();\r
+ private Map<Long, Rectangle> spanMap = new HashMap<Long, Rectangle>();\r
+ \r
+ public ClientModelImpl() {\r
+\r
+ if(Spreadsheet.DEBUG) System.out.println("SimpleContainerTableModel.init");\r
+\r
+ Map<String, Object> sheetDimensions = new THashMap<String, Object>();\r
+ Map<String, Object> headers = new THashMap<String, Object>();\r
+ Map<String, Object> excel = new THashMap<String, Object>();\r
+ Map<String, Object> sources = new THashMap<String, Object>();\r
+ Map<String, Object> sheets = new THashMap<String, Object>();\r
+ Map<String, Object> context = new THashMap<String, Object>();\r
+ Map<String, Object> mode = new THashMap<String, Object>();\r
+ Map<String, Object> states = new THashMap<String, Object>();\r
+ \r
+ sheetDimensions.put(DIMENSIONS_FIT_ROWS, false);\r
+ sheetDimensions.put(DIMENSIONS_FIT_COLS, false);\r
+ sheetDimensions.put(DIMENSIONS_COL_COUNT, 0);\r
+ sheetDimensions.put(DIMENSIONS_ROW_COUNT, 0);\r
+\r
+ excel.put(EXCEL_VISIBLE, false);\r
+\r
+ headers.put(HEADERS_COL_WIDTHS, new int[] { });\r
+ headers.put(HEADERS_ROW_HEIGHTS, new int[] { });\r
+ headers.put(HEADERS_COL_LABELS, new String[] { });\r
+ headers.put(HEADERS_ROW_LABELS, new String[] { });\r
+ \r
+ sources.put(SOURCES_AVAILABLE, new String[] { });\r
+ sources.put(SOURCES_CURRENT, "");\r
+ \r
+ properties.put(DIMENSIONS, sheetDimensions);\r
+ properties.put(HEADERS, headers);\r
+ properties.put(EXCEL, excel);\r
+ properties.put(SOURCES, sources);\r
+ properties.put(SHEETS, sheets);\r
+ properties.put(CONTEXT, context);\r
+ properties.put(MODE, mode);\r
+ properties.put(STATES, states);\r
+ \r
+ setProperty(EXCEL, EXCEL_VISIBLE, false);\r
+\r
+ }\r
+\r
+ CopyOnWriteArrayList<ClientModelListener> listeners = new CopyOnWriteArrayList<ClientModelListener>();\r
+ \r
+ @Override\r
+ public void addListener(ClientModelListener listener) {\r
+ listeners.add(listener);\r
+ listener.rows(getRows());\r
+ listener.columns(getColumns());\r
+ listener.rowLabels((String[])getPropertyAt(HEADERS, HEADERS_ROW_LABELS));\r
+ listener.columnWidths((int[])getPropertyAt(HEADERS, HEADERS_COL_WIDTHS));\r
+ listener.sources((String[])getPropertyAt(SOURCES, SOURCES_AVAILABLE), (String)getPropertyAt(SOURCES, SOURCES_CURRENT));\r
+ }\r
+\r
+ @Override\r
+ public void removeListener(ClientModelListener listener) {\r
+ listeners.remove(listener);\r
+ }\r
+ \r
+ private void fireFlush() {\r
+ for(ClientModelListener listener : listeners)\r
+ listener.flush();\r
+ }\r
+\r
+ private void fireRows() {\r
+ for(ClientModelListener listener : listeners)\r
+ listener.rows(getRows());\r
+ }\r
+\r
+ private void fireColumns() {\r
+ for(ClientModelListener listener : listeners)\r
+ listener.columns(getColumns());\r
+ }\r
+ \r
+ private void fireColumnWidths() {\r
+ for(ClientModelListener listener : listeners)\r
+ listener.columnWidths((int[])getPropertyAt(HEADERS, HEADERS_COL_WIDTHS));\r
+ }\r
+ \r
+ private void fireSources() {\r
+ for(ClientModelListener listener : listeners)\r
+ listener.sources((String[])getPropertyAt(SOURCES, SOURCES_AVAILABLE), (String)getPropertyAt(SOURCES, SOURCES_CURRENT));\r
+ }\r
+\r
+ private void fireProperty(String location, String property, Object value) {\r
+ for(ClientModelListener listener : listeners)\r
+ listener.propertyChange(location, property, value);\r
+ }\r
+\r
+ private void fireCleared(String location) {\r
+ for(ClientModelListener listener : listeners)\r
+ listener.cleared(location);\r
+ }\r
+ \r
+ @Override\r
+ public <T> T getPossiblePropertyAt(String location, String property) {\r
+ try {\r
+ T t = getPropertyAt(location, property);\r
+ return t; \r
+ } catch (Throwable e) {\r
+ return null;\r
+ }\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ public synchronized <T> T getPropertyAt(String location, String property) {\r
+\r
+ if(Spreadsheet.DEBUG) System.out.println("SimpleContainerTableModel.getPropertyAt " + location + " " + property);\r
+ \r
+ Map<String, Object> props = properties.get(location);\r
+ if(props != null) {\r
+ return (T)props.get(property);\r
+ } else {\r
+ Map<String, Object> cls = cells.get(location);\r
+ if(cls != null) {\r
+ return (T)cls.get(property);\r
+ } else {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ }\r
+ \r
+ private final Set<String> sizing = CollectionUtils.toSet(\r
+ DIMENSIONS_COL_COUNT,\r
+ DIMENSIONS_ROW_COUNT,\r
+ DIMENSIONS_FIT_COLS,\r
+ DIMENSIONS_FIT_ROWS);\r
+ \r
+ private boolean sizingProperty(String property) {\r
+ return sizing.contains(property);\r
+ }\r
+ \r
+ private boolean checkMaxRow(int row) {\r
+ if((row+1) > maxRow) {\r
+ maxRow = row+1;\r
+ return true;\r
+ } else {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ private boolean checkMaxColumn(int column) {\r
+ if((column+1) > maxColumn) {\r
+ maxColumn = column+1;\r
+ return true;\r
+ } else {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public synchronized void setProperty(final String location, final String property, final Object value) {\r
+\r
+ assert(location != null);\r
+ assert(property != null);\r
+\r
+ modLocation.add(location);\r
+ modProperty.add(property);\r
+ modValue.add(value);\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void clearAll() {\r
+ clears.addAll(cells.keySet());\r
+ }\r
+\r
+ @Override\r
+ public synchronized void clear(String location) {\r
+ if (location.startsWith("Style"))\r
+ // Nothing to do for now..\r
+ return;\r
+ clears.add(location);\r
+ }\r
+ \r
+ @Override\r
+ public synchronized void flush() {\r
+ \r
+ for(String location : clears) {\r
+ \r
+ Map<String, Object> cls = cells.remove(location);\r
+ if(cls == null) return;\r
+ \r
+ long l = SpreadsheetUtils.decodeCellCoded(location);\r
+ int row = (int)(l & 0xffffffff) - 1;\r
+ int column = (int)((l>>32) & 0xffffffff);\r
+ \r
+ if(checkMaxRow(row)) fireRows();\r
+ if(checkMaxColumn(column)) fireColumns();\r
+\r
+ removeSpan(row, column);\r
+ \r
+ fireCleared(location);\r
+ \r
+ }\r
+\r
+ for(int i=0;i<modLocation.size();i++) {\r
+ \r
+ String location = modLocation.get(i);\r
+ String property = modProperty.get(i);\r
+ Object value = modValue.get(i);\r
+\r
+ if(Spreadsheet.DEBUG) System.out.println("ClientModelImpl.setProperty " + location + " " + property + " " + value);\r
+\r
+ Map<String, Object> props = properties.get(location);\r
+ if(props != null || location.startsWith("Style")) {\r
+ if (location.startsWith("Style") && props == null) {\r
+ props = new HashMap<>();\r
+ properties.put(location, props);\r
+ }\r
+\r
+ if(sizingProperty(property)) {\r
+\r
+ int currentRows = getRows();\r
+ int currentCols = getColumns();\r
+ props.put(property, value);\r
+ if(getRows() != currentRows) fireRows();\r
+ if(getColumns() != currentCols) fireColumns();\r
+\r
+ } else {\r
+\r
+ props.put(property, value);\r
+ if(property.equals(HEADERS_COL_WIDTHS)) fireColumnWidths();\r
+ if(property.equals(SOURCES_AVAILABLE) || property.equals(SOURCES_CURRENT)) fireSources();\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ Map<String, Object> cls = cells.get(location);\r
+ if(cls == null) {\r
+ cls = new HashMap<String, Object>();\r
+ cells.put(location, cls);\r
+ }\r
+\r
+ cls.put(property, value);\r
+\r
+ long l = SpreadsheetUtils.decodeCellCoded(location);\r
+ int row = (int)(l & 0xffffffff) - 1;\r
+ int column = (int)((l>>32) & 0xffffffff);\r
+\r
+ if(checkMaxRow(row)) fireRows();\r
+ if(checkMaxColumn(column)) fireColumns();\r
+\r
+ boolean rowSpanProperty = property.equals(ROW_SPAN);\r
+ boolean columnSpanProperty = property.equals(COLUMN_SPAN);\r
+ if (rowSpanProperty || columnSpanProperty) {\r
+ Rectangle span = getRootSpan(row, column);\r
+ int size = (Integer)value;\r
+ \r
+ if (span == null) {\r
+ if (size > 1) {\r
+ if (rowSpanProperty) {\r
+ span = createSpan(row, column, size, 1);\r
+ } else {\r
+ span = createSpan(row, column, 1, size);\r
+ }\r
+ }\r
+ } else {\r
+ if (rowSpanProperty) {\r
+ span.height = size;\r
+ } else {\r
+ span.width = size;\r
+ }\r
+ if ((span.width == 1) && (span.height == 1)) {\r
+ removeSpan(row, column);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ fireProperty(location, property, value);\r
+ \r
+ }\r
+ \r
+ clears.clear();\r
+ modLocation.clear();\r
+ modProperty.clear();\r
+ modValue.clear(); \r
+ \r
+ fireFlush();\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public int getRows() {\r
+ boolean fitRows = getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_FIT_ROWS);\r
+ if(fitRows) return maxRow;\r
+ else return getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_ROW_COUNT);\r
+ }\r
+\r
+ @Override\r
+ public int getColumns() {\r
+ boolean fitCols = getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_FIT_COLS);\r
+ if(fitCols) {\r
+ return maxColumn;\r
+ }\r
+ else return getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_COL_COUNT);\r
+ }\r
+ \r
+ @Override\r
+ public int[] getColumnWidths() {\r
+ int[] data = getPropertyAt(HEADERS, HEADERS_COL_WIDTHS);\r
+ return data.clone();\r
+ }\r
+\r
+ @Override\r
+ public int[] getRowHeights() {\r
+ int[] data = getPropertyAt(HEADERS, HEADERS_ROW_HEIGHTS);\r
+ return data.clone();\r
+ }\r
+\r
+ @Override\r
+ public synchronized Collection<Pair<String, Object>> listAll(String property) {\r
+\r
+ ArrayList<Pair<String, Object>> result = new ArrayList<Pair<String, Object>>(); \r
+ \r
+ for(Map.Entry<String, Map<String, Object>> entry : properties.entrySet()) {\r
+ Object value = entry.getValue().get(property);\r
+ if(value != null) result.add(Pair.make(entry.getKey(), value));\r
+ }\r
+ for(Map.Entry<String, Map<String, Object>> entry : cells.entrySet()) {\r
+ Object value = entry.getValue().get(property);\r
+ if(value != null) result.add(Pair.make(entry.getKey(), value));\r
+ }\r
+ \r
+ return result;\r
+ \r
+ }\r
+ \r
+ private long spanKey(int row, int column) {\r
+ return (row & 0xffffffff) | (((long)(column & 0xffffffff)) << 32);\r
+ }\r
+\r
+ @Override\r
+ public synchronized Rectangle getSpan(int row, int column) {\r
+ for (Rectangle span : spanMap.values()) {\r
+ if (span.contains(column, row)) {\r
+ return new Rectangle(span);\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public synchronized List<Rectangle> getSpans() {\r
+ List<Rectangle> spans = new ArrayList<Rectangle>(spanMap.size());\r
+ for (Rectangle span : spanMap.values()) {\r
+ spans.add(new Rectangle(span));\r
+ }\r
+ return spans; \r
+ }\r
+\r
+ private Rectangle getRootSpan(int row, int column) {\r
+ return spanMap.get(spanKey(row, column));\r
+ }\r
+ \r
+ private Rectangle createSpan(int row, int column, int rowSpan, int columnSpan) {\r
+ Rectangle span = new Rectangle(column, row, columnSpan, rowSpan);\r
+ spanMap.put(spanKey(row, column), span);\r
+ return span;\r
+ }\r
+ \r
+ private void removeSpan(int row, int column) {\r
+ spanMap.remove(spanKey(row, column));\r
+ }\r
+ \r
+}\r