//----------------------------------------------------------------------------//
// //
// H a r v e s t e r //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package wixheatuser;
import java.io.*;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
/**
* Class {@code Harvester} is a specific Harvester, derived from
* WiX heat utility, meant for harvesting a directory targetted to
* a user location, and therefore using registry keys rather than
* files as KeyPath.
*
* @author Hervé Bitteur
*/
public class Harvester
{
//~ Instance fields --------------------------------------------------------
/**
* The command line arguments
* <exec executable="heat">
* <arg value="dir"/>
* <arg value="${train.dir}"/>
* <arg line="${heat.options}"/>
* <arg line="-dr adProductDirId"/>
* <arg line="-cg train.CID"/>
* <arg line="-var var.myTrain"/>
* <arg line="-out ${dev.dir}/wix/generated-train.wxs"/>
* </exec>
*/
private final CLI cli;
/** Output PrintStream */
private PrintStream ps;
/** Current indentation level in the output */
private int indent = 0;
/** All the directories processed */
private Set<DirId> dirs = new LinkedHashSet<DirId>();
//~ Constructors -----------------------------------------------------------
//-----------//
// Harvester //
//-----------//
/**
* Creates a new Harvester object.
* @param cli the Command Line Interface parameters
*/
private Harvester (CLI cli)
{
this.cli = cli;
}
//~ Methods ----------------------------------------------------------------
//------//
// main //
//------//
public static void main (String[] args)
throws FileNotFoundException, UnsupportedEncodingException, IOException
{
// Retrieve user parameters
Harvester instance = new Harvester(new CLI(args));
// Launch processing
instance.harvest();
}
//-----------------//
// groupComponents //
//-----------------//
/**
* Handle the components fragment.
*/
private void groupComponents ()
{
pl("<Fragment>");
indent++;
pl("<ComponentGroup Id=\"" + cli.cg + "\">");
indent++;
for (DirId did : dirs) {
pl("<ComponentRef Id=\"" + did + "\" />");
}
indent--;
pl("</ComponentGroup>");
indent--;
pl("</Fragment>");
}
//--------------//
// groupFolders //
//--------------//
/**
* Handle the folders fragment.
*/
private void groupFolders ()
{
pl("<Fragment>");
indent++;
pl("<DirectoryRef Id=\"" + cli.dr + "\">");
indent++;
harvestFolder(cli.dir);
indent--;
pl("</DirectoryRef>");
indent--;
pl("</Fragment>");
}
//---------//
// harvest //
//---------//
/**
* Process the whole folder hierarchy.
* @throws FileNotFoundException
* @throws UnsupportedEncodingException
* @throws IOException
*/
private void harvest ()
throws FileNotFoundException, UnsupportedEncodingException, IOException
{
System.out.println(cli.toString());
if (!cli.dir.exists()) {
throw new Error("Could not find " + cli.dir);
}
if (!cli.dir.isDirectory()) {
throw new Error("Not a directory " + cli.dir);
}
FileOutputStream os = new FileOutputStream(cli.out);
ps = new PrintStream(os, false, "UTF8");
pl("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
pl("<Wix xmlns=\"http://schemas.microsoft.com/wix/2006/wi\">");
indent++;
groupFolders(); // First fragment
groupComponents(); // Second fragment
indent--;
pl("</Wix>");
os.flush();
os.close();
}
//---------------//
// harvestFolder //
//---------------//
/**
* Process the directory provided (and recursively its subdirectories).
* @param dir the directory to process
*/
private void harvestFolder (File dir)
{
DirId did = new DirId(dir);
dirs.add(did);
pl("<Directory Id=\"" + did + "\" Name=\"" + dir.getName() + "\">");
indent++;
pl("<Component Id=\"" + did + "\" Guid=\"" + did.getUuid() + "\">");
indent++;
pl(
"<RegistryValue Root=\"HKCU\" Key=\"Software\\[Manufacturer]\\[ProductName]\" Name=\"" +
did.getPath() +
"\" Type=\"string\" Value=\"installed\" KeyPath=\"yes\" />");
pl("<RemoveFolder Id=\"" + did + "\" On=\"uninstall\" />");
Set<File> subdirs = new LinkedHashSet<File>();
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
subdirs.add(file);
} else {
FilId fid = new FilId(file);
pl(
"<File Id=\"" + fid + "\" Source=\"" + fid.getPath() +
"\" />");
}
}
indent--;
pl("</Component>");
// Handle sub directories, if any
for (File subdir : subdirs) {
harvestFolder(subdir);
}
indent--;
pl("</Directory>");
}
//----//
// pl //
//----//
/**
* Print one line on the xml output.
* @param str the line to print
*/
private void pl (String str)
{
for (int i = 0; i < indent; i++) {
ps.print(" ");
}
ps.println(str);
}
//~ Inner Classes ----------------------------------------------------------
//-----//
// CLI //
//-----//
/**
* Decodes and remembers the command line arguments.
*/
private static class CLI
{
//~ Enumerations -------------------------------------------------------
enum Status {
//~ Enumeration constant initializers ------------------------------
DIR,DR, CG,
VAR,
OUT,
NONE;
}
//~ Instance fields ----------------------------------------------------
File dir;
String dr;
String cg;
String var;
File out;
//~ Constructors -------------------------------------------------------
public CLI (String[] args)
{
// Decode all parameters
Status status = Status.DIR;
for (String arg : args) {
switch (status) {
case DIR :
dir = new File(arg);
status = Status.NONE;
break;
case NONE :
status = Status.valueOf(arg.substring(1).toUpperCase());
break;
case DR :
dr = arg;
status = Status.NONE;
break;
case CG :
cg = arg;
status = Status.NONE;
break;
case VAR :
var = arg;
status = Status.NONE;
break;
case OUT :
out = new File(arg);
status = Status.NONE;
break;
}
}
}
//~ Methods ------------------------------------------------------------
@Override
public String toString ()
{
StringBuilder sb = new StringBuilder("{CLI");
sb.append(" dir=")
.append(dir);
sb.append(" dr=")
.append(dr);
sb.append(" cg=")
.append(cg);
sb.append(" var=")
.append(var);
sb.append(" out=")
.append(out);
sb.append("}");
return sb.toString();
}
}
//-------//
// DirId //
//-------//
/**
* Handles the WIX reference to a directory.
*/
private class DirId
extends FilId
{
//~ Constructors -------------------------------------------------------
public DirId (File file)
{
super(file);
}
//~ Methods ------------------------------------------------------------
@Override
protected String getPrefix ()
{
return "dir";
}
@Override
protected String getSeparator ()
{
return "-";
}
}
//-------//
// Error //
//-------//
private class Error
extends RuntimeException
{
//~ Constructors -------------------------------------------------------
public Error (String message)
{
super(message);
}
}
//-------//
// FilId //
//-------//
/**
* Handles the WIX reference to a file.
*/
private class FilId
{
//~ Instance fields ----------------------------------------------------
protected final UUID uuid;
protected final File file;
//~ Constructors -------------------------------------------------------
public FilId (File file)
{
uuid = UUID.randomUUID();
this.file = file;
}
//~ Methods ------------------------------------------------------------
@Override
public String toString ()
{
return getPrefix() +
uuid.toString()
.replaceAll("-", "")
.toUpperCase();
}
protected String getPrefix ()
{
return "fil";
}
protected String getSeparator ()
{
return "\\";
}
String getPath ()
{
StringBuilder path = new StringBuilder();
File f = file;
while ((f != null) && !f.equals(cli.dir)) {
path.insert(0, f.getName());
path.insert(0, getSeparator());
f = f.getParentFile();
}
path.insert(0, "$(" + cli.var + ")");
return path.toString();
}
UUID getUuid ()
{
return uuid;
}
}
}