/*
* Created on 22.6.2004
*
*/
package com.idega.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author tryggvil
*
* A class to merge files from many similar sources to a single file. e.g. to merge web.xml files from many sources into one.<br>
* The file generates markers around what it merges and can use that to re-merge if changes occur.<br>
* The merged file will look something like this:<br>
* <br>
* <!-- Generated file by idegaWeb please don't modify module markers -->
* <rootxml>
* <!-- MODULE:BEGIN com.idega.core 1.0 -->
* <somexmltag>... </somexmltag>
* <!-- MODULE:END com.idega.core 1.0 -->
* </rootxml>
*
*/
public class ModuleFileMerger {
private Reader input;
private File inputFile;
private Writer output;
private File outputFile;
private String rootXMLElement="web-app";
//Sources of input Files:
private List sources;
private Map moduleMap;
private String fileHeader;
private boolean removeOlderModules=true;
/**
* @return Returns the rootXMLElement.
*/
public String getRootXMLElement() {
return this.rootXMLElement;
}
/**
* @param rootXMLElement The rootXMLElement to set.
*/
public void setRootXMLElement(String rootXMLElement) {
this.rootXMLElement = rootXMLElement;
}
/**
* @return Returns the inputFile.
*/
public File getInputFile() {
return this.inputFile;
}
/**
* @param inputFile The inputFile to set.
*/
public void setInputFile(File inputFile) {
try {
setInput(new FileReader(inputFile));
} catch (IOException e) {
e.printStackTrace();
}
this.inputFile = inputFile;
}
/**
* @return Returns the outputFile.
*/
public File getOutputFile() {
return this.outputFile;
}
/**
* Sets the outputFile. This method creates the file if it does not already exist.
* If input (setInput()) is not set it sets the input file the same as the output file if it exists.
* @param outputFile The outputFile to set.
*/
public void setOutputFile(File outputFile) {
try {
if(!outputFile.exists()){
outputFile.createNewFile();
}
else{
if(this.input==null){
//Read the contents of the outputFile (because it exists) to the input Reader
StringBuffer fileAsStringBuffer = readIntoBuffer(new FileReader(outputFile));
Reader input = new StringReader(fileAsStringBuffer.toString());
setInput(input);
}
}
setOutput(new FileWriter(outputFile));
} catch (IOException e) {
e.printStackTrace();
}
this.outputFile = outputFile;
}
/**
* @return Returns the sources.
*/
public List getMergeInSources() {
if(this.sources==null){
this.sources = new ArrayList();
}
return this.sources;
}
/**
* @param sources The sources to set.
*/
public void setMergeInSources(List sources) {
this.sources = sources;
}
public void addMergeInSourceFile(File sourceFile,String moduleIdentifier){
getMergeInSources().add(new ModuleFile(getRootXMLElement(),sourceFile,moduleIdentifier));
}
public void addMergeInSourceFile(File sourceFile,String moduleIdentifier,String moduleVersion){
getMergeInSources().add(new ModuleFile(this.getRootXMLElement(),sourceFile,moduleIdentifier,moduleVersion));
}
/**
* Read the contents of the Reader to a StringBuffer
* @param reader a reader (from file e.g.)
* @return the contents read from the reader in a StringBuffer
*/
protected StringBuffer readIntoBuffer(Reader reader){
StringBuffer outString = new StringBuffer();
int buffersize = 1000;
char[] buffer = new char[buffersize];
try {
int read = reader.read(buffer);
while(read!=-1){
outString.append(buffer,0,read);
read = reader.read(buffer);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return outString;
}
/**
* <p>
* Calls first preProcess() and then processFileMerge();
* </p>
*/
public void process() {
preProcess();
processFileMerge();
}
/**
* <p>
* Does all processing before processFileMerge() is called.
* Called first from process();
* </p>
*/
protected void preProcess(){
buildMapfromModules();
}
/**
* Execute the processing. Read the input files, search/replace and write to the output.
* This method should be called last, after all set methods are called.
*/
protected void processFileMerge(){
StringBuffer outString = new StringBuffer();
StringBuffer inString = new StringBuffer();
Reader reader = getInput();
if(reader!=null){
inString = readIntoBuffer(reader);
processContents(inString,outString);
}
else{
String fileHeader = getFileHeader();
if(fileHeader!=null){
outString.append(fileHeader);
}
outString.append("<"+getRootXMLElement()+">\n");
}
Iterator moduleIter = getMergeInSources().iterator();
while (moduleIter.hasNext()) {
ModuleFile module = (ModuleFile)moduleIter.next();
//Not include the module part again
if(!module.isHasBeenProcessed()){
appendModulePartWithComments(module,outString);
}
//replaceBuffer=new StringBuffer(outString.toString());
}
outString.append("\n</"+getRootXMLElement()+">");
PrintWriter out = new PrintWriter(getOutput());
out.write(outString.toString());
out.close();
}
protected void appendModulePartWithComments(ModuleFile module,StringBuffer outString){
String moduleId = module.getModuleIdentifier();
String moduleVersion = module.getModuleVersion();
//File inputFile = module.getSourcefile();
String modulePartBegin = "<!-- MODULE:BEGIN "+moduleId+" "+moduleVersion+" -->\n";
String modulePartEnd = "<!-- MODULE:END "+moduleId+" "+moduleVersion+" -->\n";
String moduleContents = module.getContentsWithinRootElement();
outString.append(modulePartBegin);
outString.append(moduleContents);
outString.append(modulePartEnd);
module.setHasBeenProcessed(true);
}
/**
* Process contents in the out source file (already existing and older module tags)
* @param inString
* @param outString
*/
protected void processContents(StringBuffer inString, StringBuffer outString) {
//outString = new StringBuffer();
StringBuffer semiOutBuffer = new StringBuffer();
Pattern moduleBeginPattern = Pattern.compile("<!-- MODULE:BEGIN ([\\S]+)\\s([\\S]+)\\s[^\\n\\r]+",Pattern.CASE_INSENSITIVE);
Matcher moduleBeginMatcher = moduleBeginPattern.matcher(inString);
//StringBuffer remainder = new StringBuffer();
//remainder.append(inString);
StringBuffer remainder = null;
while (moduleBeginMatcher.find()) {
// this pattern matches.
String moduleId = moduleBeginMatcher.group(1);
String version = moduleBeginMatcher.group(2);
StringBuffer oldModuleContents = new StringBuffer();
oldModuleContents.append(moduleBeginMatcher.group(0));
moduleBeginMatcher.appendReplacement(semiOutBuffer,"");
remainder = new StringBuffer();
moduleBeginMatcher.appendTail(remainder);
String regexString = "<!-- MODULE:END "+getRegExEscaped(moduleId)+" "+getRegExEscaped(version)+"[^\\n\\r]+";
Pattern moduleEndPattern = Pattern.compile(regexString,Pattern.CASE_INSENSITIVE);
Matcher moduleEndMatcher = moduleEndPattern.matcher(remainder);
ModuleFile module = (ModuleFile) this.getModuleMap().get(moduleId);
moduleEndMatcher.find();
//This must work, i.e. find() must return a result, otherwise the file is corrupt
moduleEndMatcher.appendReplacement(oldModuleContents,"$0");
//Remainder is only whats left after the MODULE:END tag
remainder = new StringBuffer();
moduleEndMatcher.appendTail(remainder);
//Begin from where the last module tag ended for the next iteration:
moduleBeginMatcher = moduleBeginPattern.matcher(remainder);
if(module!=null){
appendModulePartWithComments(module,semiOutBuffer);
}
else{
//Module not found in new sources
if(getIfRemoveOlderModules()){
//Do nothing
}
else{
semiOutBuffer.append(oldModuleContents);
}
}
}
moduleBeginMatcher.appendTail(semiOutBuffer);
//Cut </web-app> off the ending:
//replaceBuffer=new StringBuffer(outString.toString());
String out = semiOutBuffer.toString();
outString.append(out.substring(0,out.lastIndexOf("</"+getRootXMLElement()+">")));
}
/**
* Gets if to remove older module parts found
* in the source file but not found in the sources.
* Default is true.
* @return
*/
private boolean getIfRemoveOlderModules() {
// TODO Auto-generated method stub
return this.removeOlderModules;
}
/**
* Sets if to remove older module parts found
* in the source file but not found in the sources.
* @return
*/
public void setIfRemoveOlderModules(boolean value){
this.removeOlderModules=value;
}
/**
*
*/
protected void buildMapfromModules() {
Iterator moduleIter = getMergeInSources().iterator();
while (moduleIter.hasNext()) {
ModuleFile module = (ModuleFile)moduleIter.next();
String moduleId = module.getModuleIdentifier();
Map moduleMap = getModuleMap();
moduleMap.put(moduleId,module);
}
}
/**
* @return
*/
private Map getModuleMap() {
if(this.moduleMap==null){
this.moduleMap=new HashMap();
}
return this.moduleMap;
}
/**
* @return Returns the input.
*/
public Reader getInput() {
return this.input;
}
/**
* Set the Input (file or stream)
* @param input The input to set.
*/
public void setInput(Reader input) {
this.input = input;
}
/**
* @return Returns the output.
*/
public Writer getOutput() {
return this.output;
}
/**
* Set the Output (file or stream) to write the rewritten HTML to.
* @param output The output to set.
*/
public void setOutput(Writer output) {
this.output = output;
}
/**
* Gets the file Header (doctype) if it is generated
* @return
*/
public String getFileHeader(){
return this.fileHeader;
}
public void setFileHeader(String fileHeader){
this.fileHeader=fileHeader;
}
/**
* Escapes characters from the string that are reserved in regular expressions (with backslashes)
* @param inString
* @return
*/
public String getRegExEscaped(String inString){
StringBuffer out = new StringBuffer();
char[] charArray = inString.toCharArray();
for (int i = 0; i < charArray.length; i++) {
char character = charArray[i];
//TODO: Handle more special regex characters
switch (character) {
case '.':
out.append("\\.");
break;
case '$':
out.append("\\$");
break;
case '[':
out.append("\\[");
break;
case ']':
out.append("\\]");
break;
case '(':
out.append("\\(");
break;
case ')':
out.append("\\)");
break;
default:
out.append(character);
break;
}
}
return out.toString();
}
public class ModuleFile{
private Reader reader;
private String moduleIdentifier;
private String moduleVersion="1.0";
private String rootXmlElement;
private File sourcefile;
private boolean hasBeenProcessed=false;
/**
* @param sourceFile2
* @param moduleIdentifier2
*/
public ModuleFile(String xmlRootElement,File sourceFile2, String moduleIdentifier2) {
setRootXMLElement(xmlRootElement);
setSourcefile(sourceFile2);
setModuleIdentifier(moduleIdentifier2);
}
/**
* @param sourceFile2
* @param moduleIdentifier2
* @param moduleVersion2
*/
public ModuleFile(String xmlRootElement,File sourceFile2, String moduleIdentifier2, String moduleVersion2) {
setRootXMLElement(xmlRootElement);
setSourcefile(sourceFile2);
setModuleIdentifier(moduleIdentifier2);
setModuleVersion(moduleVersion2);
}
/**
* @return Returns the moduleIdentifier.
*/
public String getModuleIdentifier() {
return this.moduleIdentifier;
}
/**
* @param moduleIdentifier The moduleIdentifier to set.
*/
public void setModuleIdentifier(String moduleIdentifier) {
this.moduleIdentifier = moduleIdentifier;
}
/**
* @return Returns the moduleVersion.
*/
public String getModuleVersion() {
return this.moduleVersion;
}
/**
* @param moduleVersion The moduleVersion to set.
*/
public void setModuleVersion(String moduleVersion) {
this.moduleVersion = moduleVersion;
}
/**
* @return Returns the reader.
*/
public Reader getReader() {
return this.reader;
}
/**
* @param reader The reader to set.
*/
public void setReader(Reader reader) {
this.reader = reader;
}
/**
* @return Returns the sourcefile.
*/
public File getSourcefile() {
return this.sourcefile;
}
/**
* @param sourcefile The sourcefile to set.
*/
public void setSourcefile(File sourcefile) {
try {
setReader(new FileReader(sourcefile));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.sourcefile = sourcefile;
}
/**
* @return Returns the rootXmlElement.
*/
public String getRootXmlElement() {
return this.rootXmlElement;
}
/**
* @param rootXmlElement The rootXmlElement to set.
*/
public void setRootXmlElement(String rootXmlElement) {
this.rootXmlElement = rootXmlElement;
}
public String getFileContents(){
Reader reader = getReader();
StringBuffer sb = new StringBuffer();
int buffersize = 1000;
char[] buffer = new char[buffersize];
try {
int read = reader.read(buffer);
while(read!=-1){
sb.append(buffer,0,read);
read = reader.read(buffer);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
public String getContentsWithinRootElement(){
//Pattern p = Pattern.compile("(<a[^>]+href=\")([^#][^\"]+)([^>]+>)",Pattern.CASE_INSENSITIVE);
String fileContents = getFileContents();
String split1[] = fileContents.split("<"+getRootXMLElement()+">");
String remaining = split1[1];
String remainingsplit[] = remaining.split("</"+getRootXMLElement()+">");
String contents = remainingsplit[0];
//Pattern p = Pattern.compile("<"+getRootXMLElement()+">()</"+getRootXMLElement()">",Pattern.CASE_INSENSITIVE);
//Matcher m = p.matcher(replaceBuffer);
return contents;
}
/**
* @return Returns the hasBeenProcessed.
*/
public boolean isHasBeenProcessed() {
return this.hasBeenProcessed;
}
/**
* @param hasBeenProcessed The hasBeenProcessed to set.
*/
public void setHasBeenProcessed(boolean hasBeenProcessed) {
this.hasBeenProcessed = hasBeenProcessed;
}
}
}