/*
* Copyright 2007 - 2017 the original author or authors.
*
* 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.
*/
package net.sf.jailer.util;
import java.io.File;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Stores and restores graph layout.
*
* @author Ralf Wisser
*/
public class LayoutStorage {
/**
* Folder holding global layouts.
*/
private static final String GLOBAL_STORAGE = ".layout";
/**
* Sets position.
*
* @param rootTable root table
* @param table table
* @param position position: x, y, isFixed
*/
public static void setPosition(String rootTable, String table, double[] position) {
if (rootTable == null) {
throw new RuntimeException("rootTable is null");
}
if (table == null) {
throw new RuntimeException("table is null");
}
Map<String, double[]> posMap;
if (tempPosistions != null) {
posMap = tempPosistions;
if (position != null) {
position[2] = 1;
}
} else {
posMap = tablePositions.get(rootTable);
if (posMap == null) {
posMap = new HashMap<String, double[]>();
tablePositions.put(rootTable, posMap);
}
}
posMap.put(table, position);
}
/**
* Removes layout if it is not significant.
*
* @param rootTable root table for which the layout is
*/
public static void checkSignificance(String rootTable) {
if (tempPosistions == null) {
Map<String, double[]> layout = tablePositions.get(rootTable);
if (!isSignificant(rootTable, layout)) {
tablePositions.remove(rootTable);
} else {
globalTablePositions.put(rootTable, new HashMap<String, double[]>(layout));
}
}
}
/**
* Checks whether a layout is significant.
*
* @param rootTable root table for which the layout is
* @param layout the layout
* @return <code>true</code> if layout is significant
*/
private static boolean isSignificant(String rootTable, Map<String, double[]> layout) {
if (layout != null) {
for (String t: layout.keySet()) {
if (!t.equals(rootTable) && !"$ZOOMBOX".equals(t)) {
if (layout.get(t)[2] == 1.0) {
return true;
}
}
}
}
return false;
}
/**
* Gets file to store global layout in for a given root table.
*
* @param rootTable the root table
* @return file to store global layout in, <code>null</code> if the file cannot be created
*/
private static File getGlobalStorageFile(String rootTable) {
try {
File dir = new File(GLOBAL_STORAGE);
if (!dir.exists()) {
if (!dir.mkdir()) {
return null;
}
}
String fn = "";
for (int i = 0; i < rootTable.length(); ++i) {
char c = rootTable.charAt(i);
if (Character.isLetterOrDigit(c) || c == '.') {
fn += c;
}
}
if (fn.length() > 0) {
return new File(dir, fn);
}
return null;
} catch (Exception e) {
return null;
}
}
/**
* If storage is disabled, no persistent layout is available.
*/
public volatile static boolean enabled = true;
/**
* Gets position.
*
* @param rootTable root table
* @param table table
* @return position: x, y, isFixed
*/
public static double[] getPosition(String rootTable, String table) {
if (tempPosistions != null) {
double[] pos = tempPosistions.get(table);
return pos;
}
if (enabled) {
readGlobalLayout(rootTable);
Map<String, double[]> posMap = tablePositions.get(rootTable);
if (posMap != null) {
return posMap.get(table);
}
}
return null;
}
/**
* Gets positions of all tables for a given root-table.
*
* @param root the root table
* @return positions of all tables for a the root-table
*/
public static Map<String, double[]> getPositions(String root) {
if (tempPosistions != null) {
return tempPosistions;
}
if (enabled) {
readGlobalLayout(root);
return tablePositions.get(root);
}
return null;
}
/**
* Removes all position information for a given root table.
*
* @param root the root table
*/
public static void removeAll(String root) {
if (tempPosistions == null) {
tablePositions.remove(root);
}
}
/**
* Removes all position information.
*
* @param root the root table
*/
public static void removeAll() {
if (tempPosistions == null) {
tablePositions.clear();
globalTablePositions.clear();
}
}
/**
* Store layout into stream.
*
* @param out the stream
*/
public static void store(PrintWriter out) {
store(out, tablePositions);
try {
for (String rootTable: tablePositions.keySet()) {
Map<String, double[]> layout = globalTablePositions.get(rootTable);
if (layout == null) {
continue;
}
globalTablePositions.put(rootTable, new HashMap<String, double[]>(layout));
Map<String, Map<String, double[]>> tmp = new HashMap<String, Map<String,double[]>>();
tmp.put(rootTable, layout);
File file = getGlobalStorageFile(rootTable);
if (file != null) {
PrintWriter tout = new PrintWriter(file);
store(tout, tmp);
tout.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Reads global layout if local layout does not exist.
*
* @param rootTable
*/
private static void readGlobalLayout(String rootTable) {
// disabled in 5.3
// if (!tablePositions.containsKey(rootTable)) {
// if (!globalTablePositions.containsKey(rootTable)) {
// File file = getGlobalStorageFile(rootTable);
// Map<String, double[]> layout = null;
// if (file != null) {
// try {
// restore(file.getAbsolutePath(), false);
// if (tablePositions.get(rootTable) != null) {
// layout = new HashMap<String, double[]>(tablePositions.get(rootTable));
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// globalTablePositions.put(rootTable, layout);
// }
// Map<String, double[]> layout = globalTablePositions.get(rootTable);
// if (layout != null) {
// tablePositions.put(rootTable, new HashMap<String, double[]>(globalTablePositions.get(rootTable)));
// }
// }
}
/**
* Store layout into stream.
*
* @param out the stream
*/
public static void store(PrintWriter out, Map<String, Map<String, double[]>> positions) {
out.println(CsvFile.BLOCK_INDICATOR + "layout");
for (String root: positions.keySet()) {
Map<String, double[]> pos = positions.get(root);
for (String table: pos.keySet()) {
double[] xyf = pos.get(table);
out.println(CsvFile.encodeCell(root) + "; " + CsvFile.encodeCell(table) + "; " + xyf[0] + "; " + xyf[1] + "; " + xyf[2]);
}
}
}
/**
* Restores a layout from file
*
* @param file the file
*/
public static void restore(String file) {
restore(file, true);
}
/**
* Restores a layout from file
*
* @param file the file
*/
private static void restore(String file, boolean resetLayout) {
if (resetLayout) {
tablePositions.clear();
globalTablePositions.clear();
}
try {
File csvFile = new File(file);
if (csvFile.exists()) {
CsvFile csv = new CsvFile(csvFile, "layout");
for (CsvFile.Line line: csv.getLines()) {
if (line.cells.get(0).length() > 0) {
double x = 0.0;
double y = 0.0;
double f = 0.0;
try {
x = Double.parseDouble(line.cells.get(2));
y = Double.parseDouble(line.cells.get(3));
f = Double.parseDouble(line.cells.get(4));
setPosition(line.cells.get(0), line.cells.get(1), new double[] { x, y, f });
} catch (Exception e) {
//ignore
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Stores positions temporarily (for undo/redo).
*/
private static Map<String, double[]> tempPosistions = null;
/**
* Stores positions per root-table (local).
*/
private static Map<String, Map<String, double[]>> tablePositions = Collections.synchronizedMap(new HashMap<String, Map<String,double[]>>());
/**
* Stores positions per root-table (global).
*/
private static Map<String, Map<String, double[]>> globalTablePositions = Collections.synchronizedMap(new HashMap<String, Map<String,double[]>>());
public static void setTempStorage(Map<String, double[]> tmp) {
tempPosistions = tmp;
}
}