Will Perone

If you've been using ANT and maybe J2ME Polish to accomplish your build process for your midlets you may have noticed by now that the ability to include resources that span multiple targets is well.... broken. There is no way to cleanly have a recursive resource inclusion based on the directory path. For example lets say you have a color scheme target which may be dark or light and a screensize target which may be small or large but you have resources that are included for all small screen builds, and resources that get included for all dark builds and also resources specific to dark small screen builds. Optimally you would like to have a directory structure like the following:
res                                 <-- generic resources
res/darkcolorscheme
res/darkcolorscheme/smallscreen     <-- specific to small screen dark color scheme stuff
res/darkcolorscheme/largescreen     <-- specific to large screen dark color scheme stuff
res/lightcolorscheme
res/lightcolorscheme/smallscreen    <-- specific to small screen light color scheme stuff
res/lightcolorscheme/largescreen    <-- specific to large screen light color scheme stuff
res/smallscreen
res/largescreen
Where the lower level directories override the upper level directories (ex: res/dark/smallscreen files would override res/dark files). This would allow you to include resources without ever having duplicate files in a very clean fashion. What you have to normally do in ANT is something like the following:
res/darkcolorschemeSmallscreen        <-- contains all generic, dark color scheme and small screen resources
res/lightcolorschemeSmallscreen       <-- contains all generic, light color scheme and small screen resources
res/darkcolorschemeLargescreen        <-- contains all generic, dark color scheme and large screen resources
res/lightcolorschemeLargescreen       <-- contains all generic, light color scheme and large screen resources
What a pain. To accomplish what we would like in ANT we need to write our own custom task to process filesets. The ant code to call the task to process the res files would be the following:
<fileset dir="res" includes="**">
	<custom classname="ResInclude" classpath="resInclude">
		<param name="screensize" value="${screenSize}">
		<param name="colorscheme" value="${colorScheme}">
	</custom>
</fileset>
The java classfile for resInclude will reside in the resInclude directory. The java to process our filesets in the way we want will look like the following:
import java.io.File;
import java.util.*;

import org.apache.tools.ant.types.Parameter;
import org.apache.tools.ant.types.selectors.ExtendFileSelector;

public class ResInclude implements ExtendFileSelector {

	private Vector<File> files = new Vector<File>();

	private String []directories;

	private static String resourceDir = "res";
	private static char   pathToken = '\\';

	public void setParameters(Parameter []parameters) {
		if (directories==null) { // first time around; set everything up
			directories = new String[parameters.length];

			for (int i = 0 ; i < parameters.length; ++i) {
				directories[i] = parameters[i].getValue();
			}

			FindFiles(resourceDir);
			for (int i= 0; i < directories.length; ++i)
				RecurseSubDir(directories[i], 0, resourceDir);
		}
	}

	private void RecurseSubDir(String directory, int level, String path)
	{
		if (level >= directories.length) return;

		path+= pathToken + directory;
		//System.out.println("Trying " + path);

		// find all files in this dir
		FindFiles(path);

		// recurse inward to subdirectories
		for (int i = 0; i < directories.length; ++i)
		if (directories[i]!=directory)
		{
			RecurseSubDir(directories[i], level+1, path);
		}
	}

	/***
	 *
	 * Finds all files in a directory and includes the relevant ones to the private list
	 */
	private void FindFiles(String path)
	{
		File dir= new File(path);

		if (!dir.exists()) return;

		File[] dirFiles = dir.listFiles();

		for (int i= 0; i < dirFiles.length; ++i) {
			IncludeFile(dirFiles[i]);
		}
	}

	/**
	 * returns the directory depth of a file
	 */
	private int directoryDepth(File f) {
		String path = f.getAbsolutePath();

		int depth = 0;
		for (int i = 0; i < path.length(); ++i) {
			if (path.charAt(i)==pathToken)
				depth++;
		}
		return depth;
	}

	/**
	 * Makes sure the file is in the correct directory tree
	 */
	private boolean directoryCheck(File f) {
		String path = f.getParent();
		String relpath;
		int endIndex;
		path = path.substring(path.indexOf(resourceDir) + resourceDir.length());

		for (int start = 1; start < path.length();) {
			endIndex = path.indexOf(pathToken, start);
			if (endIndex < 0) endIndex = path.length();

			relpath = path.substring(start, endIndex);
			int i;
			for (i= 0; i < directories.length && !relpath.equals(directories[i]); ++i);
			if (i==directories.length) {// didn't match one of the valid directories
				return false;
			}
			start = endIndex+1;
		}
		return true;
	}


	/***
	 *
	 * Includes acceptable files into the file list
	 */
	private boolean IncludeFile(File file)
	{
		if (file.isDirectory()) {
			return false;
		}

		String strippedFileName = file.getName();

		// don't include files that are higher in the tree from what we have already
		int depth = directoryDepth(file);

		boolean overwrite = false;
		for (int i = 0; i < files.size(); ++i)
		{
			if (!directoryCheck(file)) return false;
			if (files.get(i).getName().equals(strippedFileName)) {
				if (directoryDepth(files.get(i)) > depth) return false;
				overwrite = true;
				files.set(i, file);
				break;
			}
		}

		if (overwrite)
		{
			System.out.println("overwrote " + strippedFileName + " with " + file);
		} else {
			files.add(file);
			System.out.println("stored " + file);
		}
		return true;
	}

	/***
	 * The main interface, returns if a file should be included
	 */
	public boolean isSelected(File baseDir, String filename, File file) {

		for (int i= 0; i < files.size(); ++i)
		if (files.get(i).getAbsolutePath().equals(file.getAbsolutePath())) {
			System.out.println("Included " + file.getAbsolutePath());
			return true;
		}

		//System.out.println("Excluded " + file.getAbsolutePath());
		return false;
	}
}
2 Comments
wamokgolrl 1 0
Hello! Good Site! Thanks you!
Sinan 2012/09/10 Contact Me0 0
You can also put the name of the folder to be igeornd into the exclude' file in .git/info'.That way, you have all your ignores in one central place and don't have to commit that change.

<- for private contact