/*******************************************************************************
* Copyright (c) 2006 RadRails.org and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.radrails.db.core;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.jruby.util.ByteList;
import org.jvyamlb.YAML;
import org.radrails.rails.core.RailsLog;
import org.radrails.rails.internal.core.RailsPlugin;
public class DatabaseYml
{
/**
* To avoid recreating/re-parsing the same database.yml file a lot
*/
private static Map<String, DatabaseYml> map = new HashMap<String, DatabaseYml>();
private static Map<String, Long> timestamps = new HashMap<String, Long>();
private Map<String, DatabaseDescriptor> fDescriptors;
private File file;
private IProject project;
private DatabaseYml(File databaseYML, IProject project)
{
this.file = databaseYML;
this.project = project;
}
public DatabaseDescriptor getDescriptor(String name)
{
return getDescriptorMap().get(name);
}
private synchronized Map<String, DatabaseDescriptor> getDescriptorMap()
{
if (fDescriptors == null)
{
if (!file.exists())
return null;
InputStream reader = null;
try
{
reader = new FileInputStream(file);
fDescriptors = load(reader);
}
catch (Exception e)
{
RailsLog.log(e);
}
finally
{
try
{
if (reader != null)
reader.close();
}
catch (IOException e)
{
// ignore
}
}
}
return fDescriptors;
}
public Collection<DatabaseDescriptor> getDescriptors()
{
if (getDescriptorMap() == null)
return Collections.emptySet();
return getDescriptorMap().values();
}
@SuppressWarnings("unchecked")
private Map<String, DatabaseDescriptor> load(InputStream stream) throws IOException
{
Map<String, DatabaseDescriptor> descriptors = new HashMap<String, DatabaseDescriptor>();
Map<ByteList, Object> configuration = (Map<ByteList, Object>) YAML.load(stream);
for (Map.Entry<ByteList, Object> entry : configuration.entrySet())
{
DatabaseDescriptor desc = new DatabaseDescriptor(entry.getKey().toString(), project);
Object children = entry.getValue();
if (children instanceof Map)
{
Map<ByteList, Object> subNodes = (Map<ByteList, Object>) children;
for (Map.Entry<ByteList, Object> subEntry : subNodes.entrySet())
{
if (subEntry != null && subEntry.getKey() != null && (!(subEntry.getValue() instanceof Map)))
{
String value = "";
if (subEntry.getValue() != null)
{
value = subEntry.getValue().toString();
}
dbMapping(desc, subEntry.getKey().toString(), value);
}
}
}
descriptors.put(desc.getName(), desc);
}
return descriptors;
}
private void dbMapping(DatabaseDescriptor descriptor, String key, String value)
{
if (descriptor == null)
{
return;
}
if (key.equals("adapter"))
{
descriptor.setAdapter(value);
}
else if (key.equals("database"))
{
descriptor.setDatabase(value);
}
else if (key.equals("host"))
{
descriptor.setHost(value);
}
else if (key.equals("username"))
{
descriptor.setUsername(value);
}
else if (key.equals("password"))
{
descriptor.setPassword(value);
}
else if (key.equals("port"))
{
descriptor.setPort(value);
}
else if (key.equals("sslcert"))
{
descriptor.setSSLCert(value);
}
else if (key.equals("sslcapath"))
{
descriptor.setSSLCapath(value);
}
else if (key.equals("sslcipher"))
{
descriptor.setSSLCipher(value);
}
else if (key.equals("socket"))
{
descriptor.setSocket(value);
}
else if (key.equals("sslkey"))
{
descriptor.setSSLKey(value);
}
else if (key.equals("schema_order"))
{
descriptor.setSchemaOrder(value);
}
else if (key.equals("dbfile"))
{
descriptor.setDBFile(value);
}
else if (key.equals("url"))
{
descriptor.setURL(value);
}
else if (key.equals("driver"))
{
descriptor.setDriver(value);
}
}
public static DatabaseYml create(IProject project)
{
synchronized (project)
{
return create(getPath(project).toFile(), project);
}
}
private static DatabaseYml create(File databaseYML, IProject project)
{
if (map.containsKey(databaseYML.getAbsolutePath()))
{
Long cachedTimestamp = timestamps.get(databaseYML.getAbsolutePath());
Long timestamp = databaseYML.lastModified();
if (timestamp.equals(cachedTimestamp)) // if timestamp hasn't changed
return map.get(databaseYML.getAbsolutePath());
}
// have no cached copy, or file has changed
DatabaseYml yml = new DatabaseYml(databaseYML, project);
map.put(databaseYML.getAbsolutePath(), yml);
Long timestamp = databaseYML.lastModified();
timestamps.put(databaseYML.getAbsolutePath(), timestamp);
return yml;
}
static IPath getPath(IProject project)
{
if (project == null)
return Path.EMPTY;
IPath root = RailsPlugin.findRailsRoot(project);
if (root == null)
root = Path.EMPTY;
if (project.getLocation() == null)
return Path.EMPTY;
return project.getLocation().append(root).append("config").append("database.yml");
}
public boolean setDescriptor(String environment, DatabaseDescriptor desc)
{
if (!file.exists())
return false;
StringBuilder modified = new StringBuilder();
BufferedReader reader = null;
boolean inDesignatedEnvironment = false;
try
{
reader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = reader.readLine()) != null)
{
if (line.startsWith(environment + ":"))
{
inDesignatedEnvironment = true;
modified.append(desc.toYML());
continue;
}
if (inDesignatedEnvironment && !Character.isWhitespace(line.charAt(0)))
{
inDesignatedEnvironment = false;
}
if (!inDesignatedEnvironment)
modified.append(line).append("\n");
}
}
catch (Exception e)
{
RailsLog.log(e);
return false;
}
finally
{
try
{
if (reader != null)
reader.close();
}
catch (IOException e)
{
// ignore
}
}
Writer writer = null;
try
{
writer = new BufferedWriter(new FileWriter(file));
// TODO Use FileWriter's newline to write the new lines?!
writer.write(modified.toString());
}
catch (IOException e)
{
RailsLog.log(e);
return false;
}
finally
{
try
{
if (writer != null)
writer.close();
}
catch (IOException e)
{
// ignore
}
}
return true;
}
}