/*
 * Copyright (c) 2005, Sam Pullara. All Rights Reserved.
 * You may modify and redistribute as long as this attribution remains.
 */

package com.sampullara.cli;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.PrintStream;
import java.lang.reflect.*;
import java.util.*;

public class Args {

    /**
     * A convenience method for parsing and automatically producing error messages.
     *
     * @param target Either an instance or a class
     * @param args   The arguments you want to parse and populate
     * @return The list of arguments that were not consumed
     */
    public static List<String> parseOrExit(Object target, String[] args) {
        try {
            return parse(target, args);
        } catch (IllegalArgumentException e) {
            System.err.println(e.getMessage());
            Args.usage(target);
            System.exit(1);
            throw e;
        }
    }

    /**
     * Parse a set of arguments and populate the target with the appropriate values.
     *
     * @param target Either an instance or a class
     * @param args   The arguments you want to parse and populate
     * @return The list of arguments that were not consumed
     */
    public static List<String> parse(Object target, String[] args) {
        List<String> arguments = new ArrayList<String>();
        arguments.addAll(Arrays.asList(args));
        Class<?> clazz;
        if (target instanceof Class) {
            clazz = (Class) target;
        } else {
            clazz = target.getClass();
            try {
                BeanInfo info = Introspector.getBeanInfo(clazz);
                for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                    processProperty(target, pd, arguments);
                }
            } catch (IntrospectionException e) {
                // If its not a JavaBean we ignore it
            }
        }

        // Check fields of 'target' class and its superclasses
      for (Class<?> currentClazz = clazz; currentClazz != null;  currentClazz = currentClazz.getSuperclass()) {
            for (Field field : currentClazz.getDeclaredFields()) {
                processField(target, field, arguments);
            }
        }

        for (String argument : arguments) {
            if (argument.startsWith("-")) {
                throw new IllegalArgumentException("Invalid argument: " + argument);
            }
        }
        return arguments;
    }

    private static void processField(Object target, Field field, List<String> arguments) {
        Argument argument = field.getAnnotation(Argument.class);
        if (argument != null) {
            boolean set = false;
            for (Iterator<String> i = arguments.iterator(); i.hasNext();) {
                String arg = i.next();
                String prefix = argument.prefix();
                String delimiter = argument.delimiter();
                if (arg.startsWith(prefix)) {
                    Object value;
                    String name = getName(argument, field);
                    String alias = getAlias(argument);
                    arg = arg.substring(prefix.length());
                    Class<?> type = field.getType();
                    if (arg.equals(name) || (alias != null && arg.equals(alias))) {
                        i.remove();
                        value = consumeArgumentValue(type, argument, i);
                        if (!set) {
                            setField(type, field, target, value, delimiter);
                        } else {
                            addArgument(type, field, target, value, delimiter);
                        }
                        set = true;
                    }
                    if (set && !type.isArray()) break;
                }
            }
            if (!set && argument.required()) {
                String name = getName(argument,  field);
                throw new IllegalArgumentException("You must set argument " + name);
            }
        }
    }

    private static void addArgument(Class type, Field field, Object target, Object value, String delimiter) {
        try {
            Object[] os = (Object[]) field.get(target);
            Object[] vs = (Object[]) getValue(type, value, delimiter);
            Object[] s = (Object[]) Array.newInstance(type.getComponentType(), os.length + vs.length);
            System.arraycopy(os, 0, s, 0, os.length);
            System.arraycopy(vs, 0, s, os.length, vs.length);
            field.set(target, s);
        } catch (IllegalAccessException iae) {
            throw new IllegalArgumentException("Could not set field " + field, iae);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
        }
    }

    private static void addPropertyArgument(Class type, PropertyDescriptor property, Object target, Object value, String delimiter) {
        try {
            Object[] os = (Object[]) property.getReadMethod().invoke(target);
            Object[] vs = (Object[]) getValue(type, value, delimiter);
            Object[] s = (Object[]) Array.newInstance(type.getComponentType(), os.length + vs.length);
            System.arraycopy(os, 0, s, 0, os.length);
            System.arraycopy(vs, 0, s, os.length, vs.length);
            property.getWriteMethod().invoke(target, (Object) s);
        } catch (IllegalAccessException iae) {
            throw new IllegalArgumentException("Could not set property " + property, iae);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
        } catch (InvocationTargetException e) {
            throw new IllegalArgumentException("Failed to validate argument " + value + " for " + property);
        }
    }

    private static void processProperty(Object target, PropertyDescriptor property, List<String> arguments) {
        Method writeMethod = property.getWriteMethod();
        if (writeMethod != null) {
            Argument argument = writeMethod.getAnnotation(Argument.class);
            if (argument != null) {
                boolean set = false;
                for (Iterator<String> i = arguments.iterator(); i.hasNext();) {
                    String arg = i.next();
                    String prefix = argument.prefix();
                    String delimiter = argument.delimiter();
                    if (arg.startsWith(prefix)) {
                        Object value;
                        String name = getName(argument, property);
                        String alias = getAlias(argument);
                        arg = arg.substring(prefix.length());
                        Class<?> type = property.getPropertyType();
                        if (arg.equals(name) || (alias != null && arg.equals(alias))) {
                            i.remove();
                            value = consumeArgumentValue(type, argument, i);
                            if (!set) {
                                setProperty(type, property, target, value, delimiter);
                            } else {
                                addPropertyArgument(type, property, target, value, delimiter);
                            }
                            set = true;
                        }
                        if (set && !type.isArray()) break;
                    }
                }
                if (!set && argument.required()) {
                    String name = getName(argument,  property);
                    throw new IllegalArgumentException("You must set argument " + name);
                }
            }
        }
    }

    /**
     * Generate usage information based on the target annotations.
     *
     * @param target An instance or class.
     */
    public static void usage(Object target) {
        usage(System.err, target);
    }

    public static void usage(Object target, String syntax) {
        usage(System.err, target, syntax);
    }

    /**
     * Generate usage information based on the target annotations.
     *
     * @param errStream A {@link java.io.PrintStream} to print the usage information to.
     * @param target An instance or class.
     */
    public static void usage(PrintStream errStream, Object target, String syntax) {
        Class<?> clazz;
        if (target instanceof Class) {
            clazz = (Class) target;
        } else {
            clazz = target.getClass();
        }
        errStream.println("Usage: " + syntax);
        for (Class<?> currentClazz = clazz; currentClazz != null;  currentClazz = currentClazz.getSuperclass()) {
          for (Field field : currentClazz.getDeclaredFields()) {
            fieldUsage(errStream, target, field);
          }
        }
        try {
            BeanInfo info = Introspector.getBeanInfo(clazz);
            for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                propertyUsage(errStream, target, pd);
            }
        } catch (IntrospectionException e) {
            // If its not a JavaBean we ignore it
        }
    }

    /**
     * Generate usage information based on the target annotations.
     *
     * @param errStream A {@link java.io.PrintStream} to print the usage information to.
     * @param target An instance or class.
     */
    public static void usage(PrintStream errStream, Object target) {
        Class<?> clazz;
        if (target instanceof Class) {
            clazz = (Class) target;
        } else {
            clazz = target.getClass();
        }
        errStream.println("Usage: " + clazz.getName());
        for (Class<?> currentClazz = clazz; currentClazz != null;  currentClazz = currentClazz.getSuperclass()) {
          for (Field field : currentClazz.getDeclaredFields()) {
            fieldUsage(errStream, target, field);
          }
        }
        try {
            BeanInfo info = Introspector.getBeanInfo(clazz);
            for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                propertyUsage(errStream, target, pd);
            }
        } catch (IntrospectionException e) {
            // If its not a JavaBean we ignore it
        }
    }

    private static void fieldUsage(PrintStream errStream, Object target, Field field) {
        Argument argument = field.getAnnotation(Argument.class);
        if (argument != null) {
            String name = getName(argument, field);
            String alias = getAlias(argument);
            String prefix = argument.prefix();
            String delimiter = argument.delimiter();
            String description = argument.description();
            makeAccessible(field);
            try {
                Object defaultValue = field.get(target);
                Class<?> type = field.getType();
                propertyUsage(errStream, prefix, name, alias, type, delimiter, description, defaultValue);
            } catch (IllegalAccessException e) {
                throw new IllegalArgumentException("Could not use thie field " + field + " as an argument field", e);
            }
        }
    }

    private static void propertyUsage(PrintStream errStream, Object target, PropertyDescriptor field) {
        Method writeMethod = field.getWriteMethod();
        if (writeMethod != null) {
            Argument argument = writeMethod.getAnnotation(Argument.class);
            if (argument != null) {
                String name = getName(argument, field);
                String alias = getAlias(argument);
                String prefix = argument.prefix();
                String delimiter = argument.delimiter();
                String description = argument.description();
                try {
                    Method readMethod = field.getReadMethod();
                    Object defaultValue;
                    if (readMethod == null) {
                        defaultValue = null;
                    } else {
                        defaultValue = readMethod.invoke(target, (Object[]) null);
                    }
                    Class<?> type = field.getPropertyType();
                    propertyUsage(errStream, prefix, name, alias, type, delimiter, description, defaultValue);
                } catch (IllegalAccessException e) {
                    throw new IllegalArgumentException("Could not use thie field " + field + " as an argument field", e);
                } catch (InvocationTargetException e) {
                    throw new IllegalArgumentException("Could not get default value for " + field, e);
                }
            }
        }

    }

    private static void propertyUsage(PrintStream errStream, String prefix, String name, String alias, Class<?> type, String delimiter, String description, Object defaultValue) {
        StringBuilder sb = new StringBuilder("  ");
        sb.append(prefix);
        sb.append(name);
        if (alias != null) {
            sb.append(" (");
            sb.append(prefix);
            sb.append(alias);
            sb.append(")");
        }
        if (type == Boolean.TYPE || type == Boolean.class) {
            sb.append(" [flag] ");
            sb.append(description);
        } else {
            sb.append(" [");
            if (type.isArray()) {
                String typeName = getTypeName(type.getComponentType());
                sb.append(typeName);
                sb.append("[");
                sb.append(delimiter);
                sb.append("]");
            } else {
                String typeName = getTypeName(type);
                sb.append(typeName);
            }
            sb.append("] ");
            sb.append(description);
            if (defaultValue != null) {
                sb.append(" (");
                if (type.isArray()) {
                    List<Object> list = new ArrayList<Object>();
                    int len = Array.getLength(defaultValue);
                    for (int i = 0; i < len; i++) {
                        list.add(Array.get(defaultValue, i));
                    }
                    sb.append(list);
                } else {
                    sb.append(defaultValue);
                }
                sb.append(")");
            }

        }
        errStream.println(sb);
    }

    private static String getTypeName(Class<?> type) {
        String typeName = type.getName();
        int beginIndex = typeName.lastIndexOf(".");
        typeName = typeName.substring(beginIndex + 1);
        return typeName;
    }

    static String getName(Argument argument, PropertyDescriptor property) {
        String name = argument.value();
        if (name.equals("")) {
            name = property.getName();
        }
        return name;

    }

    private static Object consumeArgumentValue(Class<?> type, Argument argument, Iterator<String> i) {
        Object value;
        if (type == Boolean.TYPE || type == Boolean.class) {
            value = true;
        } else {
            if (i.hasNext()) {
                value = i.next();
                i.remove();
            } else {
                throw new IllegalArgumentException("Must have a value for non-boolean argument " + argument.value());
            }
        }
        return value;
    }

    static void setProperty(Class<?> type, PropertyDescriptor property, Object target, Object value, String delimiter) {
        try {
            value = getValue(type, value, delimiter);
            property.getWriteMethod().invoke(target, value);
        } catch (IllegalAccessException iae) {
            throw new IllegalArgumentException("Could not set property " + property, iae);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
        } catch (InvocationTargetException e) {
            throw new IllegalArgumentException("Failed to validate argument " + value + " for " + property);
        }
    }

    static String getAlias(Argument argument) {
        String alias = argument.alias();
        if (alias.equals("")) {
            alias = null;
        }
        return alias;
    }

    static String getName(Argument argument, Field field) {
        String name = argument.value();
        if (name.equals("")) {
            name = field.getName();
        }
        return name;
    }

    static void setField(Class<?> type, Field field, Object target, Object value, String delimiter) {
        makeAccessible(field);
        try {
            value = getValue(type, value, delimiter);
            field.set(target, value);
        } catch (IllegalAccessException iae) {
            throw new IllegalArgumentException("Could not set field " + field, iae);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
        }
    }

    private static Object getValue(Class<?> type, Object value, String delimiter) throws NoSuchMethodException {
        if (type != String.class && type != Boolean.class && type != Boolean.TYPE) {
            if (type.isArray()) {
                String string = (String) value;
                String[] strings = string.split(delimiter);
                type = type.getComponentType();
                if (type == String.class) {
                    value = strings;
                } else {
                    Object[] array = (Object[]) Array.newInstance(type, strings.length);
                    for (int i = 0; i < array.length; i++) {
                        array[i] = createValue(type, strings[i]);
                    }
                    value = array;
                }
            } else {
                value = createValue(type, value);
            }
        }
        return value;
    }

    private static Object createValue(Class<?> type, Object value) throws NoSuchMethodException {
        Constructor<?> init = type.getDeclaredConstructor(String.class);
        try {
            value = init.newInstance(value);
        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to convert " + value + " to type " + type.getName(), e);
        }
        return value;
    }

    private static void makeAccessible(AccessibleObject ao) {
        if (ao instanceof Member) {
            Member member = (Member) ao;
            if (!Modifier.isPublic(member.getModifiers())) {
                ao.setAccessible(true);
            }
        }
    }

}


/**
Khalid
*/
package org.sikuli.slides.api.interpreters;

import java.util.concurrent.TimeUnit;

/**
 * This class converts between various units that OpenXML uses.
 * @author Khalid
 *
 */

public class UnitConverter {

	/**
	 * Converts from English Metric Units (EMUs) to pixels
	 * @param emu  Size in English Metric Unit (EMU)
	 * @return Size in pixels
	 *
	 */
    public static int emuToPixels(int emu)
    {
        if (emu != 0)
        {
        	// assuming the shape is not a child, so we divide by 9525.
            return (int)Math.round((double)emu / 9525);
        }
        else
        {
            return 0;
        }
    }
    /**
     * converts from whole points to points. In drawing ML, Whole points are specified in increments of 100.
     * Use this to convert font size from whole points to regular points.
     * @param wholePoint
     * @return
     */
    public static double WholePointsToPoints(int wholePoint){
    	return wholePoint/100;
    }
	/**
	 * Extracts and returns the time unit from a string. If the string
	 * doesn't include a unit, it returns null.
	 * @param timeString the string that contains a time unit
	 * @return the extracted time unit. If the string
	 * doesn't include a unit, it returns null.
	 */
	public static TimeUnit extractTimeUnitFromString(String timeString){

		timeString=timeString.toLowerCase();

		if(timeString.contains("minute")){
			return TimeUnit.MINUTES;
		}
		else if(timeString.contains("microsecond")){
			return TimeUnit.MICROSECONDS;
		}
		else if(timeString.contains("millisecond")){
			return TimeUnit.MILLISECONDS;
		}
		else if(timeString.contains("second")){
			return TimeUnit.SECONDS;
		}
		else if(timeString.contains("hour")){
			return TimeUnit.HOURS;
		}
		else if(timeString.toLowerCase().contains("day")){
			return TimeUnit.DAYS;
		}
		else
			return null;
	}
}


package com.sampullara.cli;

import java.util.Properties;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.IntrospectionException;

/**
 * Created by IntelliJ IDEA.
 * <p/>
 * User: samp
 * Date: Nov 11, 2007
 * Time: 3:42:27 PM
 */
public class PropertiesArgs {
    /**
     * Parse properties instead of String arguments.  Any additional arguments need to be passed some other way.
     * This is often used in a second pass when the property filename is passed on the command line.  Because of
     * required properties you must be careful to set them all in the property file.
     *
     * @param target    Either an instance or a class
     * @param arguments The properties that contain the arguments
     */
    public static void parse(Object target, Properties arguments) {
        Class clazz;
        if (target instanceof Class) {
            clazz = (Class) target;
        } else {
            clazz = target.getClass();
        }
        for (Field field : clazz.getDeclaredFields()) {
            processField(target, field, arguments);
        }
        try {
            BeanInfo info = Introspector.getBeanInfo(clazz);
            for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                processProperty(target, pd, arguments);
            }
        } catch (IntrospectionException e) {
            // If its not a JavaBean we ignore it
        }
    }

    private static void processField(Object target, Field field, Properties arguments) {
        Argument argument = field.getAnnotation(Argument.class);
        if (argument != null) {
            String name = Args.getName(argument, field);
            String alias = Args.getAlias(argument);
            Class type = field.getType();
            Object value = arguments.get(name);
            if (value == null && alias != null) {
                value = arguments.get(alias);
            }
            if (value != null) {
                if (type == Boolean.TYPE || type == Boolean.class) {
                    value = true;
                }
                Args.setField(type, field, target, value, argument.delimiter());
            } else {
                if (argument.required()) {
                    throw new IllegalArgumentException("You must set argument " + name);
                }
            }
        }
    }

    private static void processProperty(Object target, PropertyDescriptor property, Properties arguments) {
        Method writeMethod = property.getWriteMethod();
        if (writeMethod != null) {
            Argument argument = writeMethod.getAnnotation(Argument.class);
            if (argument != null) {
                String name = Args.getName(argument, property);
                String alias = Args.getAlias(argument);
                Object value = arguments.get(name);
                if (value == null && alias != null) {
                    value = arguments.get(alias);
                }
                if (value != null) {
                    Class type = property.getPropertyType();
                    if (type == Boolean.TYPE || type == Boolean.class) {
                        value = true;
                    }
                    Args.setProperty(type, property, target, value, argument.delimiter());
                } else {
                    if (argument.required()) {
                        throw new IllegalArgumentException("You must set argument " + name);
                    }
                }
            }
        }
    }
}


package org.sikuli.recorder;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.Deque;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class Zip {

	public static void unzip(File zipfile, File directory) throws IOException {
	    ZipFile zfile = new ZipFile(zipfile);
	    Enumeration<? extends ZipEntry> entries = zfile.entries();
	    while (entries.hasMoreElements()) {
	      ZipEntry entry = entries.nextElement();
	      File file = new File(directory, entry.getName());
	      if (entry.isDirectory()) {
	        file.mkdirs();
	      } else {
	        file.getParentFile().mkdirs();
	        InputStream in = zfile.getInputStream(entry);
	        try {
	          copy(in, file);
	        } finally {
	          in.close();
	        }
	      }
	    }
	  }

	// src: http://stackoverflow.com/questions/1399126/java-util-zip-recreating-directory-structure
	public static void zipDir(File directory, File zipfile) throws IOException {
		URI base = directory.toURI();
		Deque<File> queue = new LinkedList<File>();
		queue.push(directory);
		OutputStream out = new FileOutputStream(zipfile);
		Closeable res = out;
		try {
			ZipOutputStream zout = new ZipOutputStream(out);
			res = zout;
			while (!queue.isEmpty()) {
				directory = queue.pop();
				for (File kid : directory.listFiles()) {
					String name = base.relativize(kid.toURI()).getPath();
					if (kid.isDirectory()) {
						queue.push(kid);
						name = name.endsWith("/") ? name : name + "/";
						zout.putNextEntry(new ZipEntry(name));
					} else {
						zout.putNextEntry(new ZipEntry(name));
						copy(kid, zout);
						zout.closeEntry();
					}
				}
			}
		} finally {
			res.close();
		}
	}

	private static void copy(InputStream in, OutputStream out) throws IOException {
		byte[] buffer = new byte[1024];
		while (true) {
			int readCount = in.read(buffer);
			if (readCount < 0) {
				break;
			}
			out.write(buffer, 0, readCount);
		}
	}

	private static void copy(File file, OutputStream out) throws IOException {
		InputStream in = new FileInputStream(file);
		try {
			copy(in, out);
		} finally {
			in.close();
		}
	}

	private static void copy(InputStream in, File file) throws IOException {
		OutputStream out = new FileOutputStream(file);
		try {
			copy(in, out);
		} finally {
			out.close();
		}
	}
}


package org.sikuli.slides.api;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import org.sikuli.api.DesktopScreenRegion;
import org.sikuli.api.ScreenRegion;
import org.sikuli.slides.api.actions.Action;
import org.sikuli.slides.api.interpreters.DefaultInterpreter;
import org.sikuli.slides.api.interpreters.Interpreter;
import org.sikuli.slides.api.io.PPTXSlidesReader;
import org.sikuli.slides.api.io.SlidesReader;
import org.sikuli.slides.api.models.Slide;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;

/**
 * Provides static methods for executing slides. The entry point of Sikuli Slides API.
 *
 * @author Sikuli Lab
 */
public class Slides {

	static Logger logger = LoggerFactory.getLogger(Slides.class);

	private Slides(){}

	/**
	 * Execute a presentation file at a given url
	 *
	 * @param url	url to download the file
	 * @throws SlideExecutionException
	 */
	static public void execute(URL url) throws SlideExecutionException {
		checkNotNull(url);
		ScreenRegion screenRegion = new DesktopScreenRegion();
		Context context = new Context(screenRegion);
		execute(url, context);
	}

	/**
	 * Execute a presentation file at an URL using a specific {@link Context}
	 * @param url	url to download the file
	 * @param context	context
	 * @throws SlideExecutionException
	 */
	static public void execute(URL url, Context context) throws SlideExecutionException {
		checkNotNull(url);
		checkNotNull(context);
		logger.debug("execute slides with context {}", context);
		SlidesReader reader = new PPTXSlidesReader();
		List<Slide> slides;
		try {
			slides = reader.read(url);
		} catch (IOException e) {
			throw new SlideExecutionException(e);
		}

		SlidesExecutor executor = new AutomationExecutor(context);
		executor.execute(slides);
	}

	/**
	 * Execute a presentation file
	 * @param file	the presentation file
	 * @throws SlideExecutionException
	 */
	static public void execute(File file) throws SlideExecutionException {
		checkNotNull(file);
		try {
			execute(file.toURI().toURL());
		} catch (MalformedURLException e) {
			throw new SlideExecutionException(e);
		}
	}

	/**
	 * Execute a presentation file using a specific {@link Context}
	 * @param file	the presentation file
	 * @param context	the context
	 * @throws SlideExecutionException
	 */
	static public void execute(File file, Context context) throws SlideExecutionException {
		checkNotNull(file);
		checkNotNull(context);
		try {
			execute(file.toURI().toURL(), context);
		} catch (MalformedURLException e) {
			throw new SlideExecutionException(e);
		}
	}

	/**
	 * Interpret a presentation file as a list of executable actions
	 * @param file	the presentation file
	 * @return	a list of {@link Action}
	 * @throws IOException
	 */
	public static List<Action> interpret(File file) throws IOException {
		checkNotNull(file);
		Interpreter interpreter = new DefaultInterpreter();
		SlidesReader reader = new PPTXSlidesReader();
		List<Slide> slides;
		slides = reader.read(file);
		List<Action> actions = Lists.newArrayList();
		for (Slide slide : slides){
			Action action = interpreter.interpret(slide);
			actions.add(action);
			logger.info("Action interpreted: {}", action);
		}
		return actions;
	}

}


package org.sikuli.slides.api;

import java.awt.event.KeyEvent;
import java.util.List;

import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputListener;
import org.sikuli.slides.api.actions.Action;
import org.sikuli.slides.api.actions.ActionExecutionException;
import org.sikuli.slides.api.interpreters.DefaultInterpreter;
import org.sikuli.slides.api.interpreters.Interpreter;
import org.sikuli.slides.api.models.Slide;
import org.sikuli.slides.api.views.SimpleSlideViewer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;

class SlideShowExecutor
	implements SlidesExecutor {

	static Logger logger = LoggerFactory.getLogger(SlideShowExecutor.class);

	private Context context;
	private List<Action> actions;
	private List<Slide> slides;

	private SimpleSlideViewer viewer;
	private NativeInputDetector detector;

	volatile int index;
	volatile boolean pending = true;
	volatile boolean skipped = false;
	volatile boolean last = false;
	volatile boolean quit = false;
	volatile boolean autoAdvance = true;

	private int n;

	public SlideShowExecutor(Context context){
		this.context = context;
	}

	public SlideShowExecutor(){
		context = new Context();
	}

	@Override
	public void execute(List<Slide> slides) throws SlideExecutionException {
		logger.info("Executing {} slide(s)", slides.size());

		this.slides = slides;
		Interpreter interpreter = new DefaultInterpreter();
		actions = Lists.newArrayList();
		for (Slide slide : slides){
			Action action = interpreter.interpret(slide);
			actions.add(action);
			logger.debug("Action interpreted: {}", action);
		}

		detector = new NativeInputDetector();
		detector.start();
		viewer = new SimpleSlideViewer();

		context.setWaitTime(50000);

		n = slides.size();

		setCurrentSlideIndex(0);

		while (!quit){
			Action action;
			Slide slide;

			synchronized (this){
				action = actions.get(index);
				slide = slides.get(index);
				last = index == n - 1;
			}
			if (pending){
				skipped = false;
				executeSlideAction(slide, action, last);
			}

			if (!skipped && autoAdvance){
				nextSlide();
			}

			while(!pending && !quit){
				try{
					Thread.sleep(1000);
				} catch (InterruptedException e) {
				}
			}
		}
	}

	//		detector.stop();

	synchronized void setCurrentSlideIndex(int i){
		this.index = i;
		Slide slide = slides.get(i);
		viewer.setSlide(slide);
		viewer.setVisible(true);
		pending = true;
	}

	Action currentAction;
	void executeSlideAction(Slide slide, Action action, boolean last) throws SlideExecutionException{
		Context slideContext = new Context(context, slide);
		ExecutionEvent event = new ExecutionEvent(action,slideContext);

		logger.info("Slide {} of {}", slide.getNumber(), slides.size());

		try {
			currentAction = action;
			action.execute(slideContext);
		} catch (ActionExecutionException e) {
			SlideExecutionException ex = new SlideExecutionException(e);
			ex.setAction(e.getAction());
			ex.setSlide(slide);
			throw ex;
		}

		if (last)
			pending = false;
	}

	synchronized boolean hasNextSlide(){
		return index < n - 1;
	}

	synchronized void nextSlide(){
		if (index < n - 1){
			index = index + 1;
			setCurrentSlideIndex(index);
		}
	}

	synchronized void toggleAutoAdvance(){
		autoAdvance = !autoAdvance;
	}

	synchronized void skipCurrentAction(){
		if (currentAction != null){
			currentAction.stop();
			skipped = true;
		}
	}

	synchronized void prevSlide(){
		if (index > 0){
			index = index - 1;
			setCurrentSlideIndex(index);
		}
	}

	synchronized void quit(){
		skipCurrentAction();
		quit = true;
	}

	class NativeInputDetector implements NativeKeyListener, NativeMouseInputListener {

		boolean isMetaPressed(NativeKeyEvent e){
			return (e.getModifiers() & NativeKeyEvent.META_MASK) > 0;
		}
		boolean isAltPressed(NativeKeyEvent e){
			return (e.getModifiers() & NativeKeyEvent.ALT_MASK) > 0;
		}
		boolean isShiftPressed(NativeKeyEvent e){
			return (e.getModifiers() & NativeKeyEvent.SHIFT_MASK) > 0;
		}
		boolean isCtrlPressed(NativeKeyEvent e){
			return (e.getModifiers() & NativeKeyEvent.CTRL_MASK) > 0;
		}

		final public void start() {
			try {
				GlobalScreen.registerNativeHook();
			} catch (NativeHookException e1) {
				return;
			}

			GlobalScreen.getInstance().addNativeKeyListener(this);
			GlobalScreen.getInstance().addNativeMouseMotionListener(this);
			GlobalScreen.getInstance().addNativeMouseListener(this);
		}

		final public void stop(){
			GlobalScreen.getInstance().removeNativeMouseListener(this);
			GlobalScreen.getInstance().removeNativeMouseMotionListener(this);
			GlobalScreen.getInstance().removeNativeKeyListener(this);
		}

		public void nativeKeyPressed(NativeKeyEvent e) {
			if (isAltPressed(e) && isCtrlPressed(e)){

				if (e.getKeyCode() == KeyEvent.VK_RIGHT){
					skipCurrentAction();
					nextSlide();
				}else if (e.getKeyCode() == KeyEvent.VK_LEFT){
					skipCurrentAction();
					prevSlide();
				}else if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
					quit();
				}else if (e.getKeyCode() == KeyEvent.VK_A){
					toggleAutoAdvance();
				}
			}

		}
		public void nativeKeyReleased(NativeKeyEvent e) {
		}
		public void nativeKeyTyped(NativeKeyEvent e) {
		}

		public void nativeMouseClicked(NativeMouseEvent e) {
		}
		public void nativeMousePressed(NativeMouseEvent e) {
		}
		public void nativeMouseReleased(NativeMouseEvent e) {
		}
		public void nativeMouseMoved(NativeMouseEvent e) {
		}
		public void nativeMouseDragged(NativeMouseEvent e) {
		}

	}

}


package org.sikuli.slides.api.actions;

import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

import org.sikuli.slides.api.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

// execute all children actions in parallel
// execution returns when all the children have finished execution
//
public class ParallelAction extends CompoundAction {

	static Logger logger = LoggerFactory.getLogger(ParallelAction.class);

	private CountDownLatch doneSignal;

	class Worker implements Runnable {
		Action action;
		Context context;
		boolean success = true;
		ActionExecutionException exception = null;
		Worker(Action action, Context context) {
			this.action = action;
			this.context = context;
		}
		public void run() {
			try {
				action.execute(context);
				success = true;
			} catch (ActionExecutionException e) {
				logger.debug("exception thrown by {} because {} ", e.getAction(), e.getMessage());
				success = false;
				// Saves the exception so it can be re-thrown
				exception = e;
			}
			doneSignal.countDown();
		}
	}

	class BackgroundWorker extends Worker {
		BackgroundWorker(Action action, Context context) {
			super(action, context);
		}

		public void run() {
			try {
				action.execute(context);
			} catch (ActionExecutionException e) {
			}
		}
	}

	private Set<Action> backgroundSet = Sets.newHashSet();
	public void addChildAsBackground(Action child){
		backgroundSet.add(child);
		addChild(child);
	}

	public boolean hasForegroundAction(){
		for (Action action : getChildren()){
			if (!isBackground(action)){
				return true;
			}
		}
		return false;
	}

	private boolean isBackground(Action action){
		return backgroundSet.contains(action);
	}

	/**
	 * Execute and wait for execution to finish
	 */
	public void execute(Context context) throws ActionExecutionException{
		int count = 0;
		for (Action action : getChildren()){
			if (!isBackground(action)){
				count += 1;
			}
		}

		doneSignal = new CountDownLatch(count);

		List<Worker> workers = Lists.newArrayList();
		for (Action action : getChildren()){
			final Context workerContxt = new Context(context);

			if (!isBackground(action)){
				Worker worker = new Worker(action, workerContxt);
				workers.add(worker);
				new Thread(worker).start();
			}else{
				Worker worker = new BackgroundWorker(action, workerContxt);
				new Thread(worker).start();
			}
		}
		try {
			doneSignal.await();
		} catch (InterruptedException e) {

		}
		stop();

		// if any of the worker did not succeed,
		// rethrow the associated ActionExecutionException
		for (Worker worker : workers){
			if (!worker.success){
				throw worker.exception;
			}
		}
	}

	public String toString(){
		return Objects.toStringHelper(this)
				.add("actions", getChildren())
				.toString();
	}

}


package org.sikuli.slides.api.generators;

import java.io.File;
import org.sikuli.api.DesktopScreenRegion;
import org.sikuli.api.ImageTarget;
import org.sikuli.api.ScreenRegion;
import org.sikuli.api.Target;
import org.sikuli.api.robot.Mouse;
import org.sikuli.api.robot.Keyboard;
import org.sikuli.api.robot.desktop.DesktopMouse;
import org.sikuli.api.robot.desktop.DesktopKeyboard;

class GeneratedProgram {

	ScreenRegion screenRegion = new DesktopScreenRegion();
	Mouse mouse = new DesktopMouse();
	Keyboard keyboard = new DesktopKeyboard();

	// TODO: modify these settings if the default settings don't work for you
	public static int DEFAULT_WAIT_TIME = 5000;
	public static float DEFAULT_MINSCORE = 0.7f;

	// TODO: modify this path if you moved image files to another location
	public static String DEFAULT_IMAGE_DIRECTORY =  "images";

	public File findImageByName(String name){
		return new File(DEFAULT_IMAGE_DIRECTORY + File.pathSeparator + name);
	}

	// Source: Slide 1
	// Action: click
	// Argument:
	public boolean step1() {
	    	Target target = new ImageTarget(new File("image1.png"));
	    	target.setMinScore(DEFAULT_MINSCORE);
	    	ScreenRegion loc = screenRegion.find(target);
	    	if (loc != null){
	    		mouse.click(loc.getCenter());
	    		return true;
	    	}else{
	    		return false;
	    	}

	}

	// Source: Slide 2
	// Action: rightClick
	// Argument:
	public boolean step2() {
	    	Target target = new ImageTarget(new File("image2.png"));
	    	target.setMinScore(DEFAULT_MINSCORE);
	    	ScreenRegion loc = screenRegion.find(target);
	    	if (loc != null){
	    		mouse.rightClick(loc.getCenter());
	    		return true;
	    	}else{
	    		return false;
	    	}

	}

	// Source: Slide 3
	// Action: type
	// Argument: something to type
	public boolean step3() {
	    	Target target = new ImageTarget(new File("image3.png"));
	    	target.setMinScore(DEFAULT_MINSCORE);
	    	ScreenRegion loc = screenRegion.find(target);
	    	if (loc != null){
	    		mouse.click(loc.getCenter());
	    		keyboard.type("something to type");
	    		return true;
	    	}else{
	    		return false;
	    	}

	}

	// Source: Slide 4
	// Action: BrowserAction
	// Argument:
	public boolean step4() {
	    	// no action is generated for this step
	    	return true;
	}

	// Source: Slide 5
	// Action: exist
	// Argument:
	public boolean step5() {
	    	Target target = new ImageTarget(new File("image5.png"));
	    	target.setMinScore(DEFAULT_MINSCORE);
	    	ScreenRegion loc = screenRegion.find(target);
	    	return loc != null;

	}

	// Source: Slide 6
	// Action: notExist
	// Argument:
	public boolean step6() {
	    	Target target = new ImageTarget(new File("image6.png"));
	    	target.setMinScore(DEFAULT_MINSCORE);
	    	ScreenRegion loc = screenRegion.find(target);
	    	return loc == null;
	}

	// Source: Slide 7
	// Action: type
	// Argument: something to type
	public boolean step7() {
	    	Target target = new ImageTarget(new File("image7.png"));
	    	target.setMinScore(DEFAULT_MINSCORE);
	    	ScreenRegion loc = screenRegion.find(target);
	    	if (loc != null){
	    		mouse.click(loc.getCenter());
	    		keyboard.type("something to type");
	    		return true;
	    	}else{
	    		return false;
	    	}

	}

	public void executeAll(){

		boolean success1 = step1();
		boolean success2 = step2();
		boolean success3 = step3();
		boolean success4 = step4();
		boolean success5 = step5();
		boolean success6 = step6();
		boolean success7 = step7();

		// TODO: Add code to handle the return value of each step
	}

	static public void main(String... args){
		GeneratedProgram prog = new GeneratedProgram();
		prog.executeAll();
	}
}


package org.sikuli.slides.api.slideshow;

import java.awt.datatransfer.DataFlavor;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;

/**
 * This class makes it easy to drag and drop files from the operating
 * system to a Java program. Any <tt>java.awt.Component</tt> can be
 * dropped onto, but only <tt>javax.swing.JComponent</tt>s will indicate
 * the drop event with a changed border.
 *

 * To use this class, construct a new <tt>FileDrop</tt> by passing
 * it the target component and a <tt>Listener</tt> to receive notification
 * when file(s) have been dropped. Here is an example:
 *

 *

 *      JPanel myPanel = new JPanel();
 *      new FileDrop( myPanel, new FileDrop.Listener()
 *      {   public void filesDropped( java.io.File[] files )
 *          {
 *              // handle file drop
 *              ...
 *          }   // end filesDropped
 *      }); // end FileDrop.Listener
 *

 *

 * You can specify the border that will appear when files are being dragged by
 * calling the constructor with a <tt>javax.swing.border.Border</tt>. Only
 * <tt>JComponent</tt>s will show any indication with a border.
 *

 * You can turn on some debugging features by passing a <tt>PrintStream</tt>
 * object (such as <tt>System.out</tt>) into the full constructor. A <tt>null</tt>
 * value will result in no extra debugging information being output.
 *

 *
 *
I'm releasing this code into the Public Domain. Enjoy.
 *

 *
Original author: Robert Harder, rharder@usa.net

 *
2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.

 *
 * @author  Robert Harder
 * @author  rharder@users.sf.net
 * @version 1.0.1
 */
public class FileDrop
{
    private transient javax.swing.border.Border normalBorder;
    private transient java.awt.dnd.DropTargetListener dropListener;

    /** Discover if the running JVM is modern enough to have drag and drop. */
    private static Boolean supportsDnD;

    // Default border color
    private static java.awt.Color defaultBorderColor = new java.awt.Color( 0f, 0f, 1f, 0.25f );

    /**
     * Constructs a {@link FileDrop} with a default light-blue border
     * and, if <var>c</var> is a {@link java.awt.Container}, recursively
     * sets all elements contained within as drop targets, though only
     * the top level container will change borders.
     *
     * @param c Component on which files will be dropped.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.awt.Component c,
    final Listener listener )
    {   this( null,  // Logging stream
              c,     // Drop target
              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
              true, // Recursive
              listener );
    }   // end constructor

    /**
     * Constructor with a default border and the option to recursively set drop targets.
     * If your component is a <tt>java.awt.Container</tt>, then each of its children
     * components will also listen for drops, though only the parent will change borders.
     *
     * @param c Component on which files will be dropped.
     * @param recursive Recursively set children as drop targets.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.awt.Component c,
    final boolean recursive,
    final Listener listener )
    {   this( null,  // Logging stream
              c,     // Drop target
              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
              recursive, // Recursive
              listener );
    }   // end constructor

    /**
     * Constructor with a default border and debugging optionally turned on.
     * With Debugging turned on, more status messages will be displayed to
     * <tt>out</tt>. A common way to use this constructor is with
     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
     * the parameter <tt>out</tt> will result in no debugging output.
     *
     * @param out PrintStream to record debugging info or null for no debugging.
     * @param out
     * @param c Component on which files will be dropped.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.io.PrintStream out,
    final java.awt.Component c,
    final Listener listener )
    {   this( out,  // Logging stream
              c,    // Drop target
              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ),
              false, // Recursive
              listener );
    }   // end constructor

    /**
     * Constructor with a default border, debugging optionally turned on
     * and the option to recursively set drop targets.
     * If your component is a <tt>java.awt.Container</tt>, then each of its children
     * components will also listen for drops, though only the parent will change borders.
     * With Debugging turned on, more status messages will be displayed to
     * <tt>out</tt>. A common way to use this constructor is with
     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
     * the parameter <tt>out</tt> will result in no debugging output.
     *
     * @param out PrintStream to record debugging info or null for no debugging.
     * @param out
     * @param c Component on which files will be dropped.
     * @param recursive Recursively set children as drop targets.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.io.PrintStream out,
    final java.awt.Component c,
    final boolean recursive,
    final Listener listener)
    {   this( out,  // Logging stream
              c,    // Drop target
              javax.swing.BorderFactory.createMatteBorder( 2, 2, 2, 2, defaultBorderColor ), // Drag border
              recursive, // Recursive
              listener );
    }   // end constructor

    /**
     * Constructor with a specified border
     *
     * @param c Component on which files will be dropped.
     * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.awt.Component c,
    final javax.swing.border.Border dragBorder,
    final Listener listener)
    {   this(
            null,   // Logging stream
            c,      // Drop target
            dragBorder, // Drag border
            false,  // Recursive
            listener );
    }   // end constructor

    /**
     * Constructor with a specified border and the option to recursively set drop targets.
     * If your component is a <tt>java.awt.Container</tt>, then each of its children
     * components will also listen for drops, though only the parent will change borders.
     *
     * @param c Component on which files will be dropped.
     * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
     * @param recursive Recursively set children as drop targets.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.awt.Component c,
    final javax.swing.border.Border dragBorder,
    final boolean recursive,
    final Listener listener)
    {   this(
            null,
            c,
            dragBorder,
            recursive,
            listener );
    }   // end constructor

    /**
     * Constructor with a specified border and debugging optionally turned on.
     * With Debugging turned on, more status messages will be displayed to
     * <tt>out</tt>. A common way to use this constructor is with
     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
     * the parameter <tt>out</tt> will result in no debugging output.
     *
     * @param out PrintStream to record debugging info or null for no debugging.
     * @param c Component on which files will be dropped.
     * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.io.PrintStream out,
    final java.awt.Component c,
    final javax.swing.border.Border dragBorder,
    final Listener listener)
    {   this(
            out,    // Logging stream
            c,      // Drop target
            dragBorder, // Drag border
            false,  // Recursive
            listener );
    }   // end constructor

    /**
     * Full constructor with a specified border and debugging optionally turned on.
     * With Debugging turned on, more status messages will be displayed to
     * <tt>out</tt>. A common way to use this constructor is with
     * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
     * the parameter <tt>out</tt> will result in no debugging output.
     *
     * @param out PrintStream to record debugging info or null for no debugging.
     * @param c Component on which files will be dropped.
     * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
     * @param recursive Recursively set children as drop targets.
     * @param listener Listens for <tt>filesDropped</tt>.
     * @since 1.0
     */
    public FileDrop(
    final java.io.PrintStream out,
    final java.awt.Component c,
    final javax.swing.border.Border dragBorder,
    final boolean recursive,
    final Listener listener)
    {

        if( supportsDnD() )
        {   // Make a drop listener
            dropListener = new java.awt.dnd.DropTargetListener()
            {   public void dragEnter( java.awt.dnd.DropTargetDragEvent evt )
                {       log( out, "FileDrop: dragEnter event." );

                    // Is this an acceptable drag event?
                    if( isDragOk( out, evt ) )
                    {
                        // If it's a Swing component, set its border
                        if( c instanceof javax.swing.JComponent )
                        {   javax.swing.JComponent jc = (javax.swing.JComponent) c;
                            normalBorder = jc.getBorder();
                            log( out, "FileDrop: normal border saved." );
                            jc.setBorder( dragBorder );
                            log( out, "FileDrop: drag border set." );
                        }   // end if: JComponent

                        // Acknowledge that it's okay to enter
                        //evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
                        evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY );
                        log( out, "FileDrop: event accepted." );
                    }   // end if: drag ok
                    else
                    {   // Reject the drag event
                        evt.rejectDrag();
                        log( out, "FileDrop: event rejected." );
                    }   // end else: drag not ok
                }   // end dragEnter

                public void dragOver( java.awt.dnd.DropTargetDragEvent evt )
                {   // This is called continually as long as the mouse is
                    // over the drag target.
                }   // end dragOver

                public void drop( java.awt.dnd.DropTargetDropEvent evt )
                {   log( out, "FileDrop: drop event." );
                    try
                    {   // Get whatever was dropped
                        java.awt.datatransfer.Transferable tr = evt.getTransferable();

                        // Is it a file list?
                        if (tr.isDataFlavorSupported (java.awt.datatransfer.DataFlavor.javaFileListFlavor))
                        {
                            // Say we'll take it.
                            //evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
                            evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY );
                            log( out, "FileDrop: file list accepted." );

                            // Get a useful list
                            java.util.List fileList = (java.util.List)
                                tr.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor);
                            java.util.Iterator iterator = fileList.iterator();

                            // Convert list to array
                            java.io.File[] filesTemp = new java.io.File[ fileList.size() ];
                            fileList.toArray( filesTemp );
                            final java.io.File[] files = filesTemp;

                            // Alert listener to drop.
                            if( listener != null )
                                listener.filesDropped( files );

                            // Mark that drop is completed.
                            evt.getDropTargetContext().dropComplete(true);
                            log( out, "FileDrop: drop complete." );
                        }   // end if: file list
                        else // this section will check for a reader flavor.
                        {
                            // Thanks, Nathan!
                            // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
                            DataFlavor[] flavors = tr.getTransferDataFlavors();
                            boolean handled = false;
                            for (int zz = 0; zz < flavors.length; zz++) {
                                if (flavors[zz].isRepresentationClassReader()) {
                                    // Say we'll take it.
                                    //evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
                                    evt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);
                                    log(out, "FileDrop: reader accepted.");

                                    Reader reader = flavors[zz].getReaderForText(tr);

                                    BufferedReader br = new BufferedReader(reader);

                                    if(listener != null)
                                        listener.filesDropped(createFileArray(br, out));

                                    // Mark that drop is completed.
                                    evt.getDropTargetContext().dropComplete(true);
                                    log(out, "FileDrop: drop complete.");
                                    handled = true;
                                    break;
                                }
                            }
                            if(!handled){
                                log( out, "FileDrop: not a file list or reader - abort." );
                                evt.rejectDrop();
                            }
                            // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
                        }   // end else: not a file list
                    }   // end try
                    catch ( java.io.IOException io)
                    {   log( out, "FileDrop: IOException - abort:" );
                        io.printStackTrace( out );
                        evt.rejectDrop();
                    }   // end catch IOException
                    catch (java.awt.datatransfer.UnsupportedFlavorException ufe)
                    {   log( out, "FileDrop: UnsupportedFlavorException - abort:" );
                        ufe.printStackTrace( out );
                        evt.rejectDrop();
                    }   // end catch: UnsupportedFlavorException
                    finally
                    {
                        // If it's a Swing component, reset its border
                        if( c instanceof javax.swing.JComponent )
                        {   javax.swing.JComponent jc = (javax.swing.JComponent) c;
                            jc.setBorder( normalBorder );
                            log( out, "FileDrop: normal border restored." );
                        }   // end if: JComponent
                    }   // end finally
                }   // end drop

                public void dragExit( java.awt.dnd.DropTargetEvent evt )
                {   log( out, "FileDrop: dragExit event." );
                    // If it's a Swing component, reset its border
                    if( c instanceof javax.swing.JComponent )
                    {   javax.swing.JComponent jc = (javax.swing.JComponent) c;
                        jc.setBorder( normalBorder );
                        log( out, "FileDrop: normal border restored." );
                    }   // end if: JComponent
                }   // end dragExit

                public void dropActionChanged( java.awt.dnd.DropTargetDragEvent evt )
                {   log( out, "FileDrop: dropActionChanged event." );
                    // Is this an acceptable drag event?
                    if( isDragOk( out, evt ) )
                    {   //evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
                        evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY );
                        log( out, "FileDrop: event accepted." );
                    }   // end if: drag ok
                    else
                    {   evt.rejectDrag();
                        log( out, "FileDrop: event rejected." );
                    }   // end else: drag not ok
                }   // end dropActionChanged
            }; // end DropTargetListener

            // Make the component (and possibly children) drop targets
            makeDropTarget( out, c, recursive );
        }   // end if: supports dnd
        else
        {   log( out, "FileDrop: Drag and drop is not supported with this JVM" );
        }   // end else: does not support DnD
    }   // end constructor

    private static boolean supportsDnD()
    {   // Static Boolean
        if( supportsDnD == null )
        {
            boolean support = false;
            try
            {   Class arbitraryDndClass = Class.forName( "java.awt.dnd.DnDConstants" );
                support = true;
            }   // end try
            catch( Exception e )
            {   support = false;
            }   // end catch
            supportsDnD = new Boolean( support );
        }   // end if: first time through
        return supportsDnD.booleanValue();
    }   // end supportsDnD

     // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
     private static String ZERO_CHAR_STRING = "" + (char)0;
     private static File[] createFileArray(BufferedReader bReader, PrintStream out)
     {
        try {
            java.util.List list = new java.util.ArrayList();
            java.lang.String line = null;
            while ((line = bReader.readLine()) != null) {
                try {
                    // kde seems to append a 0 char to the end of the reader
                    if(ZERO_CHAR_STRING.equals(line)) continue;

                    java.io.File file = new java.io.File(new java.net.URI(line));
                    list.add(file);
                } catch (Exception ex) {
                    log(out, "Error with " + line + ": " + ex.getMessage());
                }
            }

            return (java.io.File[]) list.toArray(new File[list.size()]);
        } catch (IOException ex) {
            log(out, "FileDrop: IOException");
        }
        return new File[0];
     }
     // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.

    private void makeDropTarget( final java.io.PrintStream out, final java.awt.Component c, boolean recursive )
    {
        // Make drop target
        final java.awt.dnd.DropTarget dt = new java.awt.dnd.DropTarget();
        try
        {   dt.addDropTargetListener( dropListener );
        }   // end try
        catch( java.util.TooManyListenersException e )
        {   e.printStackTrace();
            log(out, "FileDrop: Drop will not work due to previous error. Do you have another listener attached?" );
        }   // end catch

        // Listen for hierarchy changes and remove the drop target when the parent gets cleared out.
        c.addHierarchyListener( new java.awt.event.HierarchyListener()
        {   public void hierarchyChanged( java.awt.event.HierarchyEvent evt )
            {   log( out, "FileDrop: Hierarchy changed." );
                java.awt.Component parent = c.getParent();
                if( parent == null )
                {   c.setDropTarget( null );
                    log( out, "FileDrop: Drop target cleared from component." );
                }   // end if: null parent
                else
                {   new java.awt.dnd.DropTarget(c, dropListener);
                    log( out, "FileDrop: Drop target added to component." );
                }   // end else: parent not null
            }   // end hierarchyChanged
        }); // end hierarchy listener
        if( c.getParent() != null )
            new java.awt.dnd.DropTarget(c, dropListener);

        if( recursive && (c instanceof java.awt.Container ) )
        {
            // Get the container
            java.awt.Container cont = (java.awt.Container) c;

            // Get it's components
            java.awt.Component[] comps = cont.getComponents();

            // Set it's components as listeners also
            for( int i = 0; i < comps.length; i++ )
                makeDropTarget( out, comps[i], recursive );
        }   // end if: recursively set components as listener
    }   // end dropListener

    /** Determine if the dragged data is a file list. */
    private boolean isDragOk( final java.io.PrintStream out, final java.awt.dnd.DropTargetDragEvent evt )
    {   boolean ok = false;

        // Get data flavors being dragged
        java.awt.datatransfer.DataFlavor[] flavors = evt.getCurrentDataFlavors();

        // See if any of the flavors are a file list
        int i = 0;
        while( !ok && i < flavors.length )
        {
            // BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
            // Is the flavor a file list?
            final DataFlavor curFlavor = flavors[i];
            if( curFlavor.equals( java.awt.datatransfer.DataFlavor.javaFileListFlavor ) ||
                curFlavor.isRepresentationClassReader()){
                ok = true;
            }
            // END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
            i++;
        }   // end while: through flavors

        // If logging is enabled, show data flavors
        if( out != null )
        {   if( flavors.length == 0 )
                log( out, "FileDrop: no data flavors." );
            for( i = 0; i < flavors.length; i++ )
                log( out, flavors[i].toString() );
        }   // end if: logging enabled

        return ok;
    }   // end isDragOk

    /** Outputs <tt>message</tt> to <tt>out</tt> if it's not null. */
    private static void log( java.io.PrintStream out, String message )
    {   // Log message if requested
        if( out != null )
            out.println( message );
    }   // end log

    /**
     * Removes the drag-and-drop hooks from the component and optionally
     * from the all children. You should call this if you add and remove
     * components after you've set up the drag-and-drop.
     * This will recursively unregister all components contained within
     * <var>c</var> if <var>c</var> is a {@link java.awt.Container}.
     *
     * @param c The component to unregister as a drop target
     * @since 1.0
     */
    public static boolean remove( java.awt.Component c)
    {   return remove( null, c, true );
    }   // end remove

    /**
     * Removes the drag-and-drop hooks from the component and optionally
     * from the all children. You should call this if you add and remove
     * components after you've set up the drag-and-drop.
     *
     * @param out Optional {@link java.io.PrintStream} for logging drag and drop messages
     * @param c The component to unregister
     * @param recursive Recursively unregister components within a container
     * @since 1.0
     */
    public static boolean remove( java.io.PrintStream out, java.awt.Component c, boolean recursive )
    {   // Make sure we support dnd.
        if( supportsDnD() )
        {   log( out, "FileDrop: Removing drag-and-drop hooks." );
            c.setDropTarget( null );
            if( recursive && ( c instanceof java.awt.Container ) )
            {   java.awt.Component[] comps = ((java.awt.Container)c).getComponents();
                for( int i = 0; i < comps.length; i++ )
                    remove( out, comps[i], recursive );
                return true;
            }   // end if: recursive
            else return false;
        }   // end if: supports DnD
        else return false;
    }   // end remove

/* ********  I N N E R   I N T E R F A C E   L I S T E N E R  ******** */

    /**
     * Implement this inner interface to listen for when files are dropped. For example
     * your class declaration may begin like this:
     *

     *      public class MyClass implements FileDrop.Listener
     *      ...
     *      public void filesDropped( java.io.File[] files )
     *      {
     *          ...
     *      }   // end filesDropped
     *      ...
     *

     *
     * @since 1.1
     */
    public static interface Listener {

        /**
         * This method is called when files have been successfully dropped.
         *
         * @param files An array of <tt>File</tt>s that were dropped.
         * @since 1.0
         */
        public abstract void filesDropped( java.io.File[] files );

    }   // end inner-interface Listener

/* ********  I N N E R   C L A S S  ******** */

    /**
     * This is the event that is passed to the
     * {@link FileDropListener#filesDropped filesDropped(...)} method in
     * your {@link FileDropListener} when files are dropped onto
     * a registered drop target.
     *
     *
I'm releasing this code into the Public Domain. Enjoy.

     *
     * @author  Robert Harder
     * @author  rob@iharder.net
     * @version 1.2
     */
    public static class Event extends java.util.EventObject {

        private java.io.File[] files;

        /**
         * Constructs an {@link Event} with the array
         * of files that were dropped and the
         * {@link FileDrop} that initiated the event.
         *
         * @param files The array of files that were dropped
         * @source The event source
         * @since 1.1
         */
        public Event( java.io.File[] files, Object source ) {
            super( source );
            this.files = files;
        }   // end constructor

        /**
         * Returns an array of files that were dropped on a
         * registered drop target.
         *
         * @return array of files that were dropped
         * @since 1.1
         */
        public java.io.File[] getFiles() {
            return files;
        }   // end getFiles

    }   // end inner class Event

/* ********  I N N E R   C L A S S  ******** */

    /**
     * At last an easy way to encapsulate your custom objects for dragging and dropping
     * in your Java programs!
     * When you need to create a {@link java.awt.datatransfer.Transferable} object,
     * use this class to wrap your object.
     * For example:
     *

     *      ...
     *      MyCoolClass myObj = new MyCoolClass();
     *      Transferable xfer = new TransferableObject( myObj );
     *      ...
     *

     * Or if you need to know when the data was actually dropped, like when you're
     * moving data out of a list, say, you can use the {@link TransferableObject.Fetcher}
     * inner class to return your object Just in Time.
     * For example:
     *

     *      ...
     *      final MyCoolClass myObj = new MyCoolClass();
     *
     *      TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()
     *      {   public Object getObject(){ return myObj; }
     *      }; // end fetcher
     *
     *      Transferable xfer = new TransferableObject( fetcher );
     *      ...
     *

     *
     * The {@link java.awt.datatransfer.DataFlavor} associated with
     * {@link TransferableObject} has the representation class
     * <tt>net.iharder.dnd.TransferableObject.class</tt> and MIME type
     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
     * This data flavor is accessible via the static
     * {@link #DATA_FLAVOR} property.
     *
     *
     *
I'm releasing this code into the Public Domain. Enjoy.

     *
     * @author  Robert Harder
     * @author  rob@iharder.net
     * @version 1.2
     */
    public static class TransferableObject implements java.awt.datatransfer.Transferable
    {
        /**
         * The MIME type for {@link #DATA_FLAVOR} is
         * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
         *
         * @since 1.1
         */
        public final static String MIME_TYPE = "application/x-net.iharder.dnd.TransferableObject";

        /**
         * The default {@link java.awt.datatransfer.DataFlavor} for
         * {@link TransferableObject} has the representation class
         * <tt>net.iharder.dnd.TransferableObject.class</tt>
         * and the MIME type
         * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
         *
         * @since 1.1
         */
        public final static java.awt.datatransfer.DataFlavor DATA_FLAVOR =
            new java.awt.datatransfer.DataFlavor( FileDrop.TransferableObject.class, MIME_TYPE );

        private Fetcher fetcher;
        private Object data;

        private java.awt.datatransfer.DataFlavor customFlavor;

        /**
         * Creates a new {@link TransferableObject} that wraps <var>data</var>.
         * Along with the {@link #DATA_FLAVOR} associated with this class,
         * this creates a custom data flavor with a representation class
         * determined from data.getClass() and the MIME type
         * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
         *
         * @param data The data to transfer
         * @since 1.1
         */
        public TransferableObject( Object data )
        {   this.data = data;
            this.customFlavor = new java.awt.datatransfer.DataFlavor( data.getClass(), MIME_TYPE );
        }   // end constructor

        /**
         * Creates a new {@link TransferableObject} that will return the
         * object that is returned by <var>fetcher</var>.
         * No custom data flavor is set other than the default
         * {@link #DATA_FLAVOR}.
         *
         * @see Fetcher
         * @param fetcher The {@link Fetcher} that will return the data object
         * @since 1.1
         */
        public TransferableObject( Fetcher fetcher )
        {   this.fetcher = fetcher;
        }   // end constructor

        /**
         * Creates a new {@link TransferableObject} that will return the
         * object that is returned by <var>fetcher</var>.
         * Along with the {@link #DATA_FLAVOR} associated with this class,
         * this creates a custom data flavor with a representation class <var>dataClass</var>
         * and the MIME type
         * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
         *
         * @see Fetcher
         * @param dataClass The {@link java.lang.Class} to use in the custom data flavor
         * @param fetcher The {@link Fetcher} that will return the data object
         * @since 1.1
         */
        public TransferableObject( Class dataClass, Fetcher fetcher )
        {   this.fetcher = fetcher;
            this.customFlavor = new java.awt.datatransfer.DataFlavor( dataClass, MIME_TYPE );
        }   // end constructor

        /**
         * Returns the custom {@link java.awt.datatransfer.DataFlavor} associated
         * with the encapsulated object or <tt>null</tt> if the {@link Fetcher}
         * constructor was used without passing a {@link java.lang.Class}.
         *
         * @return The custom data flavor for the encapsulated object
         * @since 1.1
         */
        public java.awt.datatransfer.DataFlavor getCustomDataFlavor()
        {   return customFlavor;
        }   // end getCustomDataFlavor

    /* ********  T R A N S F E R A B L E   M E T H O D S  ******** */

        /**
         * Returns a two- or three-element array containing first
         * the custom data flavor, if one was created in the constructors,
         * second the default {@link #DATA_FLAVOR} associated with
         * {@link TransferableObject}, and third the
         * {@link java.awt.datatransfer.DataFlavor.stringFlavor}.
         *
         * @return An array of supported data flavors
         * @since 1.1
         */
        public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors()
        {
            if( customFlavor != null )
                return new java.awt.datatransfer.DataFlavor[]
                {   customFlavor,
                    DATA_FLAVOR,
                    java.awt.datatransfer.DataFlavor.stringFlavor
                };  // end flavors array
            else
                return new java.awt.datatransfer.DataFlavor[]
                {   DATA_FLAVOR,
                    java.awt.datatransfer.DataFlavor.stringFlavor
                };  // end flavors array
        }   // end getTransferDataFlavors

        /**
         * Returns the data encapsulated in this {@link TransferableObject}.
         * If the {@link Fetcher} constructor was used, then this is when
         * the {@link Fetcher#getObject getObject()} method will be called.
         * If the requested data flavor is not supported, then the
         * {@link Fetcher#getObject getObject()} method will not be called.
         *
         * @param flavor The data flavor for the data to return
         * @return The dropped data
         * @since 1.1
         */
        public Object getTransferData( java.awt.datatransfer.DataFlavor flavor )
        throws java.awt.datatransfer.UnsupportedFlavorException, java.io.IOException
        {
            // Native object
            if( flavor.equals( DATA_FLAVOR ) )
                return fetcher == null ? data : fetcher.getObject();

            // String
            if( flavor.equals( java.awt.datatransfer.DataFlavor.stringFlavor ) )
                return fetcher == null ? data.toString() : fetcher.getObject().toString();

            // We can't do anything else
            throw new java.awt.datatransfer.UnsupportedFlavorException(flavor);
        }   // end getTransferData

        /**
         * Returns <tt>true</tt> if <var>flavor</var> is one of the supported
         * flavors. Flavors are supported using the equals(...) method.
         *
         * @param flavor The data flavor to check
         * @return Whether or not the flavor is supported
         * @since 1.1
         */
        public boolean isDataFlavorSupported( java.awt.datatransfer.DataFlavor flavor )
        {
            // Native object
            if( flavor.equals( DATA_FLAVOR ) )
                return true;

            // String
            if( flavor.equals( java.awt.datatransfer.DataFlavor.stringFlavor ) )
                return true;

            // We can't do anything else
            return false;
        }   // end isDataFlavorSupported

    /* ********  I N N E R   I N T E R F A C E   F E T C H E R  ******** */

        /**
         * Instead of passing your data directly to the {@link TransferableObject}
         * constructor, you may want to know exactly when your data was received
         * in case you need to remove it from its source (or do anyting else to it).
         * When the {@link #getTransferData getTransferData(...)} method is called
         * on the {@link TransferableObject}, the {@link Fetcher}'s
         * {@link #getObject getObject()} method will be called.
         *
         * @author Robert Harder
         * @copyright 2001
         * @version 1.1
         * @since 1.1
         */
        public static interface Fetcher
        {
            /**
             * Return the object being encapsulated in the
             * {@link TransferableObject}.
             *
             * @return The dropped object
             * @since 1.1
             */
            public abstract Object getObject();
        }   // end inner interface Fetcher

    }   // end class TransferableObject

}   // end class FileDrop


package org.sikuli.slides.api.actions;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Timer;
import java.util.TimerTask;

import org.sikuli.slides.api.Context;
import com.google.common.base.Objects;

// execute all children actions in parallel
// execution returns when all the children have finished execution
//
public class RetryAction extends ChainedAction {

	public RetryAction(Action action, long timeout, long interval){
		setChild(checkNotNull(action));
		this.timeout = timeout;
		this.interval = interval;
	}

	long timeout;
	long interval;
	volatile boolean timesupFlag;
	volatile boolean stopFlag;

	/**
	 * Execute and wait for execution to finish
	 */
	public void execute(Context context) throws ActionExecutionException{
		Action action = checkNotNull(getChild());

		// start a timer that will set the timesup flag to true
		Timer timer = new Timer();
		TimerTask task = new TimerTask(){
			@Override
			public void run() {
				timesupFlag = true;
			}
		};
		if (timeout < Long.MAX_VALUE){
			timer.schedule(task, timeout);
		}

		ActionExecutionException exception = null;

		timesupFlag = false;
		stopFlag = false;
		while(!timesupFlag && !stopFlag){
			try{
				action.execute(context);
				return;
			}catch(ActionExecutionException e){
				exception = e;
			}
			synchronized (this){
				try {
					this.wait(interval);
				} catch (InterruptedException e) {
				}
			}
		}

		if (timesupFlag){
			// execution does not succeed before timeout
			// rethrow the exception
			if (exception != null)
				throw exception;
		}
	}

	@Override
	public void stop(){
		stopFlag = true;
		getChild().stop();
		synchronized(this){
			this.notify();
		}
	}

	public String toString(){
		return Objects.toStringHelper(this)
				.add("child", getChild())
				.toString();
	}

}


package org.sikuli.slides.api.sikuli;

import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.sikuli.api.ScreenRegion;
import org.sikuli.api.Target;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;

interface SearchStrategy {
	public ScreenRegion perform(ScreenRegion screenRegion);
}

public class CrossSearchStrategy implements SearchStrategy {
	private static final Logger logger = LoggerFactory.getLogger(CrossSearchStrategy.class);
	static private final int widthFactor=30;
	static private final int heightFactor=30;

	BufferedImage contextImage;
	Rectangle targetRect;

	public CrossSearchStrategy(BufferedImage contextImage, Rectangle targetRect) {
		super();
		this.contextImage = contextImage;
		this.targetRect = targetRect;
	}

	@Override
	public ScreenRegion perform(ScreenRegion screenRegion){
		List<Hypothesis> hs = generateHypotheses(contextImage, targetRect);
		logger.trace("generated {} hypotheses", hs.size());
		ScreenRegion ret = testHypotheses(screenRegion, hs);
		return ret;
	}

	private static List<Hypothesis> generateHypotheses(BufferedImage contextImage, Rectangle targetRect) {
		Rectangle bounds = new Rectangle(0,0,contextImage.getWidth(), contextImage.getHeight());
		List<Rectangle> regions = generateLargerRegions(targetRect, bounds);
		sortBySize(regions);

		// make hypotheses
		List<Hypothesis> hs = Lists.newArrayList();
		for (Rectangle r : regions){
			ContextTargetHypothesis h = new ContextTargetHypothesis(contextImage, targetRect, r);
			hs.add(h);
		}
		return hs;
	}

	static public List<Rectangle> generateLargerRegions(Rectangle seed, Rectangle bounds){
		List<Rectangle> result = Lists.newArrayList();
		result.add(seed);
		result.addAll(generateLargerRegionsAbove(seed, bounds));
		result.addAll(generateLargerRegionsBelow(seed, bounds));
		result.addAll(generateLargerRegionsLeft(seed, bounds));
		result.addAll(generateLargerRegionsRight(seed, bounds));
		return result;
	}

	// sort a list of rectangles by their sizes in ascending order
	static public List<Rectangle> sortBySize(List<Rectangle> list){
		List<Rectangle> result = Lists.newArrayList();
		Collections.sort(list, new Comparator<Rectangle>(){
			@Override
			public int compare(Rectangle r1, Rectangle r2) {
				return (r1.height * r1.width) - (r2.height * r2.width);
			}
		});
		return result;
	}

	static private ScreenRegion testHypotheses(ScreenRegion screenRegion, List<Hypothesis> hypotheses){
		for(Hypothesis hypothesis : hypotheses){

			Target target = hypothesis.getTarget();
			target.setMinScore(0.8f);

			List<ScreenRegion> lookupRegion = screenRegion.findAll(target);
			logger.trace("test: {} ... found {} matches", hypothesis, lookupRegion.size());

			if(lookupRegion.size()>1){
				continue;
			}
			else if(lookupRegion.size()==1){
				ScreenRegion rawResult = lookupRegion.get(0);
				return hypothesis.interpretResult(rawResult);
			}else {
				return null;
			}
		}
		return null;
	}

	static public List<Rectangle> generateLargerRegionsAbove(Rectangle seed, Rectangle bounds){
		List<Rectangle> result = new ArrayList<Rectangle>();
		Rectangle r = new Rectangle(seed);
		for(int i=0;i<20;i++){
			r.y -= heightFactor;
			r.height += heightFactor;
			r = bounds.intersection(r); // keep the rectangle within bounds
			result.add(new Rectangle(r));
			if (r.y <= bounds.y)
				break;
		}
		return result;
	}

	static public List<Rectangle> generateLargerRegionsBelow(Rectangle seed, Rectangle bounds){
		List<Rectangle> result = new ArrayList<Rectangle>();
		Rectangle r = new Rectangle(seed);
		for(int i=0;i<20;i++){
			r.height += heightFactor;
			r = bounds.intersection(r); // keep the rectangle within bounds
			result.add(new Rectangle(r));
			if (r.y + r.height >= bounds.y + bounds.height)
				break;
		}
		return result;
	}

	static public List<Rectangle> generateLargerRegionsRight(Rectangle seed, Rectangle bounds){
		List<Rectangle> result = new ArrayList<Rectangle>();
		Rectangle r = new Rectangle(seed);
		for(int i=0;i<20;i++){
			r.width += widthFactor;
			r = bounds.intersection(r); // keep the rectangle within bounds
			result.add(new Rectangle(r));
			if (r.x + r.width >= bounds.x + bounds.width)
				break;
		}
		return result;
	}

	static public List<Rectangle> generateLargerRegionsLeft(Rectangle seed, Rectangle bounds){
		List<Rectangle> result = new ArrayList<Rectangle>();
		Rectangle r = new Rectangle(seed);
		for(int i=0;i<20;i++){
			r.x -= widthFactor;
			r.width += widthFactor;
			r = bounds.intersection(r); // keep the rectangle within bounds
			result.add(new Rectangle(r));
			if (r.x <= bounds.x)
				break;
		}
		return result;
	}
}