/*******************************************************************************
* Copyright (c) 2016 comtel inc.
*
* 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 org.jfxvnc.app.persist;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
import java.util.Properties;
import javax.annotation.PreDestroy;
import org.slf4j.LoggerFactory;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.FloatProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.scene.control.Accordion;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TitledPane;
import javafx.scene.control.ToggleGroup;
import javafx.scene.paint.Color;
public class SessionContext {
private final static org.slf4j.Logger logger = LoggerFactory.getLogger(SessionContext.class);
private String name;
private Path propPath;
private ObservableList<HistoryEntry> history;
private ObservableMap<String, Property<?>> bindings;
private final Properties props = new Properties();
private Path streamPath;
public SessionContext() {
setSession(SessionContext.class.getName());
}
public void setSession(String name) {
this.name = name;
propPath = FileSystems.getDefault().getPath(System.getProperty("user.home"), "." + name + ".properties");
streamPath = FileSystems.getDefault().getPath(System.getProperty("user.home"), "." + name + ".history");
}
public Properties getProperties() {
return props;
}
public void loadSession() {
if (Files.exists(propPath, LinkOption.NOFOLLOW_LINKS)) {
try (InputStream is = Files.newInputStream(propPath, StandardOpenOption.READ)) {
props.load(is);
} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
}
}
@PreDestroy
public void saveSession() {
logger.debug("save session");
try (OutputStream outStream = Files.newOutputStream(propPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
props.store(outStream, name + " session properties");
} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
try {
saveHistory();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
public void bind(final BooleanProperty property, final String propertyName) {
String value = props.getProperty(propertyName);
if (value != null) {
property.set(Boolean.valueOf(value));
}
property.addListener(o -> {
props.setProperty(propertyName, property.getValue().toString());
});
}
@SuppressWarnings("unchecked")
public void bind(final ObjectProperty<?> property, final String propertyName, Class<?> type) {
String value = props.getProperty(propertyName);
if (value != null) {
if (type.getName().equals(Color.class.getName())) {
((ObjectProperty<Color>) property).set(Color.valueOf(value));
} else if (type.getName().equals(String.class.getName())) {
((ObjectProperty<String>) property).set(value);
} else {
((ObjectProperty<Object>) property).set(value);
}
}
property.addListener(o -> {
props.setProperty(propertyName, property.getValue().toString());
});
}
public void bind(final DoubleProperty property, final String propertyName) {
String value = props.getProperty(propertyName);
if (value != null) {
property.set(Double.valueOf(value));
}
property.addListener(o -> {
props.setProperty(propertyName, property.getValue().toString());
});
}
public void bind(final ToggleGroup toggleGroup, final String propertyName) {
try {
String value = props.getProperty(propertyName);
if (value != null) {
int selectedToggleIndex = Integer.parseInt(value);
toggleGroup.selectToggle(toggleGroup.getToggles().get(selectedToggleIndex));
}
} catch (Exception ignored) {
}
toggleGroup.selectedToggleProperty().addListener(o -> {
if (toggleGroup.getSelectedToggle() == null) {
props.remove(propertyName);
} else {
props.setProperty(propertyName, Integer.toString(toggleGroup.getToggles().indexOf(toggleGroup.getSelectedToggle())));
}
});
}
public void bind(final Accordion accordion, final String propertyName) {
Object selectedPane = props.getProperty(propertyName);
for (TitledPane tp : accordion.getPanes()) {
if (tp.getText() != null && tp.getText().equals(selectedPane)) {
accordion.setExpandedPane(tp);
break;
}
}
accordion.expandedPaneProperty().addListener((ov, t, expandedPane) -> {
if (expandedPane != null) {
props.setProperty(propertyName, expandedPane.getText());
}
});
}
public void bind(final ComboBox<?> combo, final String propertyName) {
try {
String value = props.getProperty(propertyName);
if (value != null) {
int index = Integer.parseInt(value);
combo.getSelectionModel().select(index);
}
} catch (Exception ignored) {
}
combo.getSelectionModel().selectedIndexProperty().addListener(o -> {
props.setProperty(propertyName, Integer.toString(combo.getSelectionModel().getSelectedIndex()));
});
}
public void bind(final StringProperty property, final String propertyName) {
String value = props.getProperty(propertyName);
if (value != null) {
property.set(value);
}
property.addListener(o -> {
props.setProperty(propertyName, property.getValue());
});
}
/**
* session scope bindings
*
* @return
*/
public ObservableMap<String, Property<?>> getBindings() {
if (bindings == null) {
bindings = FXCollections.observableHashMap();
}
return bindings;
}
/**
* add session scope binding (Property.getName() required)
*
* @param value
*/
public void addBinding(Property<?> value) {
if (value.getName() == null || value.getName().isEmpty()) {
throw new IllegalArgumentException("property name must not be empty");
}
getBindings().put(value.getName(), value);
}
public Optional<Property<?>> getBinding(String key) {
return Optional.ofNullable(getBindings().get(key));
}
public Optional<ObjectProperty<?>> getObjectBinding(String key) {
Optional<Property<?>> b = getBinding(key);
if (!b.isPresent() || !ObjectProperty.class.isInstance(b.get())) {
return Optional.empty();
}
return Optional.of((ObjectProperty<?>) b.get());
}
public Optional<BooleanProperty> getBooleanBinding(String key) {
Optional<Property<?>> b = getBinding(key);
if (!b.isPresent() || !BooleanProperty.class.isInstance(b.get())) {
return Optional.empty();
}
return Optional.of((BooleanProperty) b.get());
}
public Optional<IntegerProperty> getIntegerBinding(String key) {
Optional<Property<?>> b = getBinding(key);
if (!b.isPresent() || !IntegerProperty.class.isInstance(b.get())) {
return Optional.empty();
}
return Optional.of((IntegerProperty) b.get());
}
public Optional<StringProperty> getStringBinding(String key) {
Optional<Property<?>> b = getBinding(key);
if (!b.isPresent() || !StringProperty.class.isInstance(b.get())) {
return Optional.empty();
}
return Optional.of((StringProperty) b.get());
}
public Optional<DoubleProperty> getDoubleBinding(String key) {
Optional<Property<?>> b = getBinding(key);
if (!b.isPresent() || !DoubleProperty.class.isInstance(b.get())) {
return Optional.empty();
}
return Optional.of((DoubleProperty) b.get());
}
public Optional<FloatProperty> getFloatBinding(String key) {
Optional<Property<?>> b = getBinding(key);
if (!b.isPresent() || !FloatProperty.class.isInstance(b.get())) {
return Optional.empty();
}
return Optional.of((FloatProperty) b.get());
}
public ObservableList<HistoryEntry> getHistory() {
if (history == null) {
history = FXCollections.observableArrayList();
loadHistory();
}
return history;
}
private void loadHistory() {
history.clear();
if (!Files.exists(streamPath, LinkOption.NOFOLLOW_LINKS)) {
logger.debug("no stream exist ({})", streamPath);
return;
}
logger.info("load history ({})", streamPath);
try (InputStream inStream = Files.newInputStream(streamPath, StandardOpenOption.READ)) {
try (ObjectInputStream oStream = new ObjectInputStream(inStream)) {
Object o = null;
while (inStream.available() > 0 && (o = oStream.readObject()) != null) {
HistoryEntry dev = (HistoryEntry) o;
logger.debug("read dev: {}", dev);
if (!history.contains(dev)) {
history.add(dev);
} else {
logger.error("device already exist ({})", dev);
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
try {
Files.deleteIfExists(streamPath);
} catch (IOException e1) {
}
}
}
private void saveHistory() throws IOException {
if (history.isEmpty()) {
Files.deleteIfExists(streamPath);
return;
}
try (
OutputStream outStream = Files.newOutputStream(streamPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
try (ObjectOutputStream historyStream = new ObjectOutputStream(outStream)) {
for (HistoryEntry h : history) {
historyStream.writeObject(h);
}
}
}
}
}