package com.revolsys.io.connection;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import com.revolsys.beans.PropertyChangeSupportProxy;
import com.revolsys.collection.map.MapEx;
import com.revolsys.collection.map.Maps;
import com.revolsys.io.FileUtil;
import com.revolsys.record.io.format.json.Json;
import com.revolsys.spring.resource.FileSystemResource;
import com.revolsys.spring.resource.PathResource;
import com.revolsys.spring.resource.Resource;
import com.revolsys.util.Property;
public abstract class AbstractConnectionRegistry<C extends Connection>
implements ConnectionRegistry<C>, PropertyChangeListener {
private ConnectionRegistryManager<ConnectionRegistry<C>> connectionManager;
private final Map<String, String> connectionNames = new TreeMap<>();
private Map<String, C> connections;
private File directory;
private final String fileExtension;
private final String name;
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private boolean readOnly;
private boolean visible = true;
public AbstractConnectionRegistry(
final ConnectionRegistryManager<? extends ConnectionRegistry<C>> connectionManager,
final String name, final boolean visible, final boolean readOnly,
final Resource directoryResource, final String fileExtension) {
this.name = name;
this.fileExtension = fileExtension;
setConnectionManager(connectionManager);
setVisible(visible);
setReadOnly(readOnly);
setDirectory(directoryResource);
init();
}
protected synchronized void addConnection(final String name, final C connection) {
if (connection != null && name != null) {
final String lowerName = name.toLowerCase();
final C existingConnection = this.connections.get(lowerName);
removeConnection(existingConnection);
this.connectionNames.put(lowerName, name);
this.connections.put(lowerName, connection);
if (connection instanceof PropertyChangeSupportProxy) {
final PropertyChangeSupportProxy proxy = (PropertyChangeSupportProxy)connection;
final PropertyChangeSupport propertyChangeSupport = proxy.getPropertyChangeSupport();
if (propertyChangeSupport != null) {
propertyChangeSupport.addPropertyChangeListener(this);
}
}
final int index = getConnectionIndex(name);
this.propertyChangeSupport.fireIndexedPropertyChange("connections", index, null, connection);
this.propertyChangeSupport.fireIndexedPropertyChange("children", index, null, connection);
}
}
@Override
public C getConnection(final String connectionName) {
if (Property.hasValue(connectionName)) {
return this.connections.get(connectionName.toLowerCase());
} else {
return null;
}
}
protected File getConnectionFile(final Connection connection, final boolean useOriginalFile) {
File connectionFile = null;
if (useOriginalFile) {
connectionFile = connection.getConnectionFile();
}
if (connectionFile == null) {
final String connectionName = connection.getName();
connectionFile = getConnectionFile(connectionName);
}
return connectionFile;
}
protected File getConnectionFile(final String name) {
if (Property.hasValue(name)) {
if (!this.directory.exists()) {
if (isReadOnly()) {
return null;
} else if (!this.directory.mkdirs()) {
return null;
}
}
final String fileName = FileUtil.toSafeName(name) + "." + this.fileExtension;
final File file = new File(this.directory, fileName);
return file;
} else {
return null;
}
}
protected int getConnectionIndex(final String name) {
final String lowerName = name.toLowerCase();
final int index = new ArrayList<>(this.connectionNames.keySet()).indexOf(lowerName);
return index;
}
@Override
public ConnectionRegistryManager<ConnectionRegistry<C>> getConnectionManager() {
return this.connectionManager;
}
public synchronized String getConnectionName(final C connection) {
for (final Entry<String, C> entry : this.connections.entrySet()) {
if (entry.getValue() == connection) {
final String lowerName = entry.getKey();
return this.connectionNames.get(lowerName);
}
}
return null;
}
protected String getConnectionName(final MapEx config, final File connectionFile,
final boolean requireUniqueNames) {
String name = config.getString("name");
if (connectionFile != null && !Property.hasValue(name)) {
name = FileUtil.getBaseName(connectionFile);
}
if (requireUniqueNames) {
name = getUniqueName(name);
}
config.put("name", name);
return name;
}
@Override
public List<String> getConnectionNames() {
final List<String> names = new ArrayList<>(this.connectionNames.values());
return names;
}
@Override
public List<C> getConnections() {
return new ArrayList<>(this.connections.values());
}
public File getDirectory() {
return this.directory;
}
@Override
public String getFileExtension() {
return this.fileExtension;
}
@Override
public String getName() {
return this.name;
}
@Override
public PropertyChangeSupport getPropertyChangeSupport() {
return this.propertyChangeSupport;
}
protected String getUniqueName(String name) {
int i = 1;
String newName = name;
while (getConnection(newName) != null) {
newName = name + i;
i++;
}
name = newName;
return name;
}
@Override
public void importConnection(final File file) {
if (file != null && file.isFile()) {
loadConnection(file, true);
}
}
protected synchronized void init() {
this.connections = new TreeMap<>();
initDo();
}
protected void initDo() {
if (this.directory != null && this.directory.isDirectory()) {
for (final File connectionFile : FileUtil.getFilesByExtension(this.directory,
this.fileExtension, "rgobject")) {
loadConnection(connectionFile, false);
}
}
}
@Override
public boolean isReadOnly() {
return this.readOnly;
}
@Override
public boolean isVisible() {
return this.visible;
}
protected abstract C loadConnection(final File connectionFile, boolean importConnection);
@Override
public C newConnection(final Map<String, ? extends Object> connectionParameters) {
final String name = Maps.getString(connectionParameters, "name");
final File file = getConnectionFile(name);
if (file != null && (!file.exists() || file.canRead())) {
Json.writeMap(connectionParameters, file, true);
return loadConnection(file, false);
}
return null;
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
this.propertyChangeSupport.firePropertyChange(event);
}
@Override
public boolean removeConnection(final Connection connection) {
if (connection == null) {
return false;
} else {
final String name = connection.getName();
return removeConnection(name, connection);
}
}
public boolean removeConnection(final String name) {
final C connection = getConnection(name);
return removeConnection(connection);
}
protected synchronized boolean removeConnection(final String name, final Connection connection) {
if (connection != null && name != null) {
final String lowerName = name.toLowerCase();
final C existingConnection = this.connections.get(lowerName);
if (existingConnection == connection) {
final int index = getConnectionIndex(name);
this.connectionNames.remove(lowerName);
this.connections.remove(lowerName);
if (connection instanceof PropertyChangeSupportProxy) {
final PropertyChangeSupportProxy proxy = (PropertyChangeSupportProxy)connection;
final PropertyChangeSupport propertyChangeSupport = proxy.getPropertyChangeSupport();
if (propertyChangeSupport != null) {
propertyChangeSupport.removePropertyChangeListener(this);
}
}
this.propertyChangeSupport.fireIndexedPropertyChange("connections", index, connection,
null);
this.propertyChangeSupport.fireIndexedPropertyChange("children", index, connection, null);
if (this.directory != null && !this.readOnly) {
final File file = existingConnection.getConnectionFile();
FileUtil.deleteDirectory(file);
}
return true;
}
}
return false;
}
public void save() {
saveDo(true);
}
public void saveAs(final Resource directory) {
setDirectory(directory);
saveDo(false);
}
public void saveAs(final Resource parentDirectory, final String directoryName) {
final Resource connectionsDirectory = parentDirectory.newChildResource(directoryName);
saveAs(connectionsDirectory);
}
private void saveDo(final boolean useOriginalFile) {
for (final Connection connection : this.connections.values()) {
final File connectionFile = getConnectionFile(connection, useOriginalFile);
final String name = connection.getName();
if (Property.hasValue(name)) {
connection.writeToFile(connectionFile);
} else {
throw new IllegalArgumentException("Connection must have a name");
}
}
}
@Override
public void setConnectionManager(
final ConnectionRegistryManager<? extends ConnectionRegistry<C>> connectionManager) {
if (this.connectionManager != connectionManager) {
if (this.connectionManager != null) {
this.propertyChangeSupport.removePropertyChangeListener(connectionManager);
}
this.connectionManager = (ConnectionRegistryManager)connectionManager;
if (connectionManager != null) {
this.propertyChangeSupport.addPropertyChangeListener(connectionManager);
}
}
}
protected void setDirectory(final Resource directoryResource) {
if (directoryResource instanceof FileSystemResource) {
final FileSystemResource fileResource = (FileSystemResource)directoryResource;
final File directory = fileResource.getFile();
boolean readOnly = isReadOnly();
if (!readOnly) {
if (directoryResource.exists()) {
readOnly = !directory.canWrite();
} else if (directory.mkdirs()) {
readOnly = false;
} else {
readOnly = true;
}
}
setReadOnly(readOnly);
this.directory = directory;
} else if (directoryResource instanceof PathResource) {
final PathResource pathResource = (PathResource)directoryResource;
final File directory = pathResource.getFile();
boolean readOnly = isReadOnly();
if (!readOnly) {
if (directoryResource.exists()) {
readOnly = !directory.canWrite();
} else if (directory.mkdirs()) {
readOnly = false;
} else {
readOnly = true;
}
}
setReadOnly(readOnly);
this.directory = directory;
} else {
setReadOnly(true);
this.directory = null;
}
}
public void setReadOnly(final boolean readOnly) {
if (this.isReadOnly() && !readOnly) {
throw new IllegalArgumentException("Cannot make a read only registry not read only");
}
this.readOnly = readOnly;
}
public void setVisible(final boolean visible) {
this.visible = visible;
}
@Override
public String toString() {
return getName();
}
}