/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.core.db.ant.dbsetup;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import mazz.i18n.Msg;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.Environment;
import org.rhq.core.db.ant.DbAntI18NFactory;
import org.rhq.core.db.ant.DbAntI18NResourceKeys;
/**
* An ant task to combine multiple DBSetup XML files into a single file.
*/
public class DBSetupCombiner extends BaseFileSetTask {
private static final Msg MSG = DbAntI18NFactory.getMsg();
private static final String XML_PREFIX = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
private File m_destFile = null;
private String m_order = null;
private String m_name = null;
private ArrayList<Environment.Variable> sysProps = new ArrayList<Environment.Variable>();
public void setDestfile(File destFile) {
m_destFile = destFile;
}
public void setOrder(String order) {
m_order = order;
}
public void setName(String name) {
m_name = name;
}
/**
* Support subelements to set System properties e.g <sysproperty key="foo" value="bar" /> After the task has
* completed, the system properties will be reverted to their old values (of if the system property didn't exist
* before, it will be removed).
*
* @param sysprop
*/
public void addSysproperty(Environment.Variable sysprop) {
sysProps.add(sysprop);
}
/**
* @see org.apache.tools.ant.Task#execute()
*/
public void execute() throws BuildException {
validateAttributes();
// being able to set system properties can be useful to set JDBC LoggerDriver system properties
// but remember the old values so we can revert back to them after the task finishes
Properties old_sysprops = new Properties(); // old values for keys that existed
List<String> nonexistent_sysprops = new ArrayList<String>(); // keys that didn't exist in system properties
for (Environment.Variable env_var : sysProps) {
String old_value = System.setProperty(env_var.getKey(), env_var.getValue());
if (old_value == null) {
nonexistent_sysprops.add(env_var.getKey());
} else {
old_sysprops.put(env_var.getKey(), old_value);
}
}
try {
List<File> files_to_combine;
PrintWriter pw = null;
// First get all the files, unsorted
files_to_combine = getAllFiles();
// Reorder according to user-specified order
if (m_order != null) {
List<String> order_list = new ArrayList<String>();
StringTokenizer tok = new StringTokenizer(m_order, ", \n\r\t");
while (tok.hasMoreTokens()) {
order_list.add(tok.nextToken());
}
// based on the specified order, we need to sort all the files
Collections.sort(files_to_combine, new OrderComparator(order_list));
}
try {
// Open the destFile
pw = new PrintWriter(new FileWriter(m_destFile));
// Write the prefix
writePrefix(pw);
// Concatenate the files
catFiles(files_to_combine, pw);
// Write the suffix
writeSuffix(pw);
} catch (IOException ioe) {
throw new BuildException(ioe);
} finally {
if (pw != null) {
try {
pw.close();
} catch (Exception e) {
}
}
}
} finally {
// revert back to the old system properties
for (String name : nonexistent_sysprops) {
System.clearProperty(name);
}
for (Map.Entry old_entry : old_sysprops.entrySet()) {
System.setProperty((String) old_entry.getKey(), (String) old_entry.getValue());
}
}
return;
}
/**
* This method concatenates a series of files to a single destination, stripping out XML prefixes and top-level
* elements.
*
* @param files A list of the files to be concatenated
* @param pw Destination for writes.
*
* @throws IOException
*/
private void catFiles(List<File> files, PrintWriter pw) throws IOException {
BufferedReader in = null;
try {
for (int i = 0; i < files.size(); i++) {
File current_file = files.get(i);
String current_filename = current_file.getName();
in = new BufferedReader(new InputStreamReader(new FileInputStream(current_file)));
pw.println("<!-- BEGIN: " + current_filename + " -->");
String line;
while ((line = in.readLine()) != null) {
String trimmed_line = line.trim();
// Skip XML directive lines, and top-level element lines
if (trimmed_line.startsWith("<?") || trimmed_line.startsWith("</dbsetup>")) {
continue;
} else if (trimmed_line.startsWith("<dbsetup")) {
while (trimmed_line.indexOf(">") == -1) {
trimmed_line = in.readLine();
if (trimmed_line == null) {
break;
}
}
continue;
}
// print the original line
pw.println(line);
}
in.close();
in = null;
pw.println("<!-- END: " + current_filename + " -->");
}
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {
}
}
}
return;
}
/**
* Makes sure this task's attributes are valid.
*
* @see org.rhq.core.db.ant.dbsetup.BaseFileSetTask#validateAttributes()
*/
protected void validateAttributes() throws BuildException {
super.validateAttributes();
if (m_destFile == null) {
throw new BuildException(MSG.getMsg(DbAntI18NResourceKeys.TASK_MISSING_ATTRIB, getTaskName(), "destFile"));
}
if (m_name == null) {
throw new BuildException(MSG.getMsg(DbAntI18NResourceKeys.TASK_MISSING_ATTRIB, getTaskName(), "name"));
}
}
private void writePrefix(PrintWriter pw) throws IOException {
pw.println(XML_PREFIX);
pw.println("<dbsetup name=\"" + this.m_name + "\">");
}
private void writeSuffix(PrintWriter pw) throws IOException {
pw.println("</dbsetup>");
}
private class OrderComparator implements Comparator {
private List<String> orderList;
/**
* Creates a new {@link OrderComparator} object.
*
* @param order
*/
public OrderComparator(List<String> order) {
orderList = order;
}
/**
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Object o1, Object o2) {
// Make sure they are both files, otherwise we'll just say they're always "equal"
if ((o1 instanceof File) && (o2 instanceof File)) {
// Get basenames for files
String f1 = ((File) o1).getName();
String f2 = ((File) o2).getName();
// Find the index at which this basename occurs in the order
int idx1 = findIndex(f1);
int idx2 = findIndex(f2);
// If one was found and the other was not, the one
// that was found is automatically "less than" the one that was not.
if ((idx1 == -1) && (idx2 != -1)) {
return Integer.MAX_VALUE;
} else if ((idx2 == -1) && (idx1 != -1)) {
return Integer.MIN_VALUE;
} else {
// If they were both found, or if they were both -1, just return the difference
return (idx1 - idx2);
}
}
return 0;
}
private int findIndex(String fname) {
int indexOfLongestMatch = -1;
int lengthOfLongestMatch = 0;
String possibleMatch;
int possibleMatchLen;
for (int i = 0; i < orderList.size(); i++) {
possibleMatch = orderList.get(i);
possibleMatchLen = possibleMatch.length();
if (fname.startsWith(possibleMatch) && (possibleMatchLen > lengthOfLongestMatch)) {
indexOfLongestMatch = i;
lengthOfLongestMatch = possibleMatchLen;
}
}
return indexOfLongestMatch;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object o) {
if (o instanceof OrderComparator) {
OrderComparator oc = (OrderComparator) o;
return ((this.orderList == null) && (oc.orderList == null)) || this.orderList.equals(oc.orderList);
}
return false;
}
}
}