package org.springframework.roo.addon.propfiles;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.process.manager.MutableFile;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.support.util.FileUtils;
/**
* Provides property file configuration operations.
*
* @author Ben Alex
* @author Stefan Schmidt
* @since 1.0
*/
@Component
@Service
public class PropFileOperationsImpl implements PropFileOperations {
private static final boolean CHANGE_EXISTING = true;
private static final boolean SORTED = true;
@Reference private FileManager fileManager;
@Reference private ProjectOperations projectOperations;
public void addProperties(final LogicalPath propertyFilePath,
final String propertyFilename,
final Map<String, String> properties, final boolean sorted,
final boolean changeExisting) {
manageProperty(propertyFilePath, propertyFilename, properties, sorted,
changeExisting);
}
public void addPropertyIfNotExists(final LogicalPath propertyFilePath,
final String propertyFilename, final String key, final String value) {
manageProperty(propertyFilePath, propertyFilename, asMap(key, value),
!SORTED, !CHANGE_EXISTING);
}
public void addPropertyIfNotExists(final LogicalPath propertyFilePath,
final String propertyFilename, final String key,
final String value, final boolean sorted) {
manageProperty(propertyFilePath, propertyFilename, asMap(key, value),
sorted, !CHANGE_EXISTING);
}
private Map<String, String> asMap(final String key, final String value) {
final Map<String, String> properties = new HashMap<String, String>();
properties.put(key, value);
return properties;
}
public void changeProperty(final LogicalPath propertyFilePath,
final String propertyFilename, final String key, final String value) {
manageProperty(propertyFilePath, propertyFilename, asMap(key, value),
!SORTED, CHANGE_EXISTING);
}
public void changeProperty(final LogicalPath propertyFilePath,
final String propertyFilename, final String key,
final String value, final boolean sorted) {
manageProperty(propertyFilePath, propertyFilename, asMap(key, value),
sorted, CHANGE_EXISTING);
}
public Map<String, String> getProperties(
final LogicalPath propertyFilePath, final String propertyFilename) {
Validate.notNull(propertyFilePath, "Property file path required");
Validate.notBlank(propertyFilename, "Property filename required");
final String filePath = projectOperations.getPathResolver()
.getIdentifier(propertyFilePath, propertyFilename);
final Properties props = new Properties();
try {
if (fileManager.exists(filePath)) {
loadProperties(props, new BufferedInputStream(
new FileInputStream(filePath)));
}
else {
throw new IllegalStateException("Properties file not found");
}
}
catch (final IOException ioe) {
throw new IllegalStateException(ioe);
}
final Map<String, String> result = new HashMap<String, String>();
for (final Object key : props.keySet()) {
result.put(key.toString(), props.getProperty(key.toString()));
}
return Collections.unmodifiableMap(result);
}
public String getProperty(final LogicalPath propertyFilePath,
final String propertyFilename, final String key) {
Validate.notNull(propertyFilePath, "Property file path required");
Validate.notBlank(propertyFilename, "Property filename required");
Validate.notBlank(key, "Key required");
final String filePath = projectOperations.getPathResolver()
.getIdentifier(propertyFilePath, propertyFilename);
MutableFile mutableFile = null;
final Properties props = new Properties();
if (fileManager.exists(filePath)) {
mutableFile = fileManager.updateFile(filePath);
loadProperties(props, mutableFile.getInputStream());
}
else {
return null;
}
return props.getProperty(key);
}
public SortedSet<String> getPropertyKeys(
final LogicalPath propertyFilePath, final String propertyFilename,
final boolean includeValues) {
Validate.notNull(propertyFilePath, "Property file path required");
Validate.notBlank(propertyFilename, "Property filename required");
final String filePath = projectOperations.getPathResolver()
.getIdentifier(propertyFilePath, propertyFilename);
final Properties props = new Properties();
try {
if (fileManager.exists(filePath)) {
loadProperties(props, new BufferedInputStream(
new FileInputStream(filePath)));
}
else {
throw new IllegalStateException("Properties file not found");
}
}
catch (final IOException ioe) {
throw new IllegalStateException(ioe);
}
final SortedSet<String> result = new TreeSet<String>();
for (final Object key : props.keySet()) {
String info = key.toString();
if (includeValues) {
info += " = " + props.getProperty(key.toString());
}
result.add(info);
}
return result;
}
public boolean isPropertiesCommandAvailable() {
return projectOperations.isFocusedProjectAvailable();
}
public Properties loadProperties(final InputStream inputStream) {
final Properties properties = new Properties();
if (inputStream != null) {
loadProperties(properties, inputStream);
}
return properties;
}
private void loadProperties(final Properties props,
final InputStream inputStream) {
try {
props.load(inputStream);
}
catch (final IOException e) {
throw new IllegalStateException("Could not load properties", e);
}
finally {
IOUtils.closeQuietly(inputStream);
}
}
public Properties loadProperties(final String filename,
final Class<?> loadingClass) {
return loadProperties(FileUtils.getInputStream(loadingClass, filename));
}
private void manageProperty(final LogicalPath propertyFilePath,
final String propertyFilename,
final Map<String, String> properties, final boolean sorted,
final boolean changeExisting) {
Validate.notNull(propertyFilePath, "Property file path required");
Validate.notBlank(propertyFilename, "Property filename required");
Validate.notNull(properties, "Property map required");
final String filePath = projectOperations.getPathResolver()
.getIdentifier(propertyFilePath, propertyFilename);
MutableFile mutableFile = null;
Properties props;
if (sorted) {
props = new Properties() {
private static final long serialVersionUID = 1L;
// Override the keys() method to order the keys alphabetically
@SuppressWarnings("all")
public synchronized Enumeration keys() {
final Object[] keys = keySet().toArray();
Arrays.sort(keys);
return new Enumeration() {
int i = 0;
public boolean hasMoreElements() {
return i < keys.length;
}
public Object nextElement() {
return keys[i++];
}
};
}
};
}
else {
props = new Properties();
}
if (fileManager.exists(filePath)) {
mutableFile = fileManager.updateFile(filePath);
loadProperties(props, mutableFile.getInputStream());
}
else {
// Unable to find the file, so let's create it
mutableFile = fileManager.createFile(filePath);
}
boolean saveNeeded = false;
for (final Entry<String, String> entry : properties.entrySet()) {
final String key = entry.getKey();
final String newValue = entry.getValue();
final String existingValue = props.getProperty(key);
if (existingValue == null || !existingValue.equals(newValue)
&& changeExisting) {
props.setProperty(key, newValue);
saveNeeded = true;
}
}
if (saveNeeded) {
storeProps(props, mutableFile.getOutputStream(), "Updated at "
+ new Date());
}
}
public void removeProperty(final LogicalPath propertyFilePath,
final String propertyFilename, final String key) {
Validate.notNull(propertyFilePath, "Property file path required");
Validate.notBlank(propertyFilename, "Property filename required");
Validate.notBlank(key, "Key required");
final String filePath = projectOperations.getPathResolver()
.getIdentifier(propertyFilePath, propertyFilename);
MutableFile mutableFile = null;
final Properties props = new Properties();
if (fileManager.exists(filePath)) {
mutableFile = fileManager.updateFile(filePath);
loadProperties(props, mutableFile.getInputStream());
}
else {
throw new IllegalStateException("Properties file not found");
}
props.remove(key);
storeProps(props, mutableFile.getOutputStream(), "Updated at "
+ new Date());
}
private void storeProps(final Properties props,
final OutputStream outputStream, final String comment) {
Validate.notNull(outputStream, "OutputStream required");
try {
props.store(outputStream, comment);
}
catch (final IOException e) {
throw new IllegalStateException("Could not store properties", e);
}
finally {
IOUtils.closeQuietly(outputStream);
}
}
}