gt: 		if (aid == null) {
context: package org.area515.resinprinter.job;
  
  import java.awt.Color;
  import java.awt.Graphics2D;
  import java.awt.Paint;
  import java.awt.geom.AffineTransform;
  import java.awt.image.BufferedImage;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.ExecutionException;
  import java.util.concurrent.Future;
  import java.util.concurrent.TimeUnit;
  import java.util.concurrent.locks.ReentrantLock;
  
  import javax.script.Bindings;
  import javax.script.Invocable;
  import javax.script.ScriptContext;
  import javax.script.ScriptEngine;
  import javax.script.ScriptException;
  import javax.xml.bind.JAXBException;
  
  import org.apache.commons.io.FilenameUtils;
  import org.apache.logging.log4j.LogManager;
  import org.apache.logging.log4j.Logger;
  import org.area515.resinprinter.display.ControlFlow;
  import org.area515.resinprinter.display.InappropriateDeviceException;
  import org.area515.resinprinter.exception.NoPrinterFoundException;
  import org.area515.resinprinter.exception.SliceHandlingException;
  import org.area515.resinprinter.job.Customizer.PrinterStep;
  import org.area515.resinprinter.job.render.CurrentImageRenderer;
  import org.area515.resinprinter.job.render.RenderingCache;
  import org.area515.resinprinter.job.render.RenderingContext;
  import org.area515.resinprinter.notification.NotificationManager;
  import org.area515.resinprinter.printer.Printer;
  import org.area515.resinprinter.printer.PrinterConfiguration;
  import org.area515.resinprinter.printer.SlicingProfile;
  import org.area515.resinprinter.printer.SlicingProfile.InkConfig;
  import org.area515.resinprinter.server.HostProperties;
  import org.area515.resinprinter.server.Main;
  import org.area515.resinprinter.services.CustomizerService;
  import org.area515.resinprinter.slice.StlError;
  import org.area515.util.Log4jUtil;
  import org.area515.util.TemplateEngine;
  
  public abstract class AbstractPrintFileProcessor<G,E> implements PrintFileProcessor<G,E>{
  	private static final Logger logger = LogManager.getLogger();
  	public static final String EXPOSURE_TIMER = "exposureTime";
  	
  	public static class DataAid {
  		private Integer renderingSlice;
  		public Printer printer;
  		public PrintJob printJob;
  		public PrinterConfiguration configuration;
  		public SlicingProfile slicingProfile;
  		public InkConfig inkConfiguration;
  		public double xPixelsPerMM;
  		public double yPixelsPerMM;
  		public int xResolution;
  		public int yResolution;
  		public double sliceHeight;
  		public InkDetector inkDetector;
  		public long currentSliceTime;
  		public Paint maskPaint;
  		public boolean optimizeWithPreviewMode;
  		private AffineTransform affineTransform;
  		public RenderingCache cache = new RenderingCache();
  		public Customizer customizer;
  		public Customizer originalCustomizer;
  		public CurrentImageRenderer currentlyRenderingImage;
  		
  		public DataAid(PrintJob printJob) throws JobManagerException {
  			this.printJob = printJob;
  			printer = printJob.getPrinter();
  			printJob.setStartTime(System.currentTimeMillis());
  		    configuration = printer.getConfiguration();
  			slicingProfile = configuration.getSlicingProfile();
  			inkConfiguration = slicingProfile.getSelectedInkConfig();
  			xPixelsPerMM = slicingProfile.getDotsPermmX();
  			yPixelsPerMM = slicingProfile.getDotsPermmY();
  			xResolution = slicingProfile.getxResolution();
  			yResolution = slicingProfile.getyResolution();
  			optimizeWithPreviewMode = false;
  			customizer = printJob.getCustomizer();
  			
  			if (customizer == null) {
  				customizer = new Customizer();
  				customizer.setNextStep(PrinterStep.PerformHeader);
  				customizer.setNextSlice(0);
  				customizer.setPrintableName(FilenameUtils.getBaseName(printJob.getJobFile().getName()));
  				customizer.setPrintableExtension(FilenameUtils.getExtension(printJob.getJobFile().getName()));
  				customizer.setPrinterName(printer.getName());
  				customizer.setName(printJob.getJobFile().getName() + "." + printer.getName());
  				Customizer otherCustomizer = CustomizerService.INSTANCE.getCustomizers().get(customizer.getName());
  				if (otherCustomizer != null) {
  					customizer.setName(customizer.getName() + "." + System.currentTimeMillis());
  				}
  				CustomizerService.INSTANCE.addOrUpdateCustomizer(customizer);
  			}
  			if (customizer.getNextStep() == null) {
  				customizer.setNextStep(PrinterStep.PerformHeader);
  			}
  			if (customizer.getZScale() == null) {
  				customizer.setZScale(1.0);
  			}
  			
  			//We must make sure our customizer is perfectly setup at this point, everyone should be able to depend on our customizer after this setup process
  			try {
  				originalCustomizer = HostProperties.deepCopyJAXB(customizer, Customizer.class);
  			} catch (JAXBException e) {
  				throw new JobManagerException("Couldn't copy customizer", e);
  			}
  			
  			//This file processor requires an ink configuration
  			if (inkConfiguration == null) {
  				throw new JobManagerException("Your printer doesn't have a selected ink configuration.");
  			}
  			
  			//TODO: how do I integrate slicingProfile.getLiftDistance()
  			sliceHeight = inkConfiguration.getSliceHeight();
  		}
  		
  		//This puts the customizer back into the original state and saves it so that completed prints will start from the original location that the user requested.
  		public void saveOriginalCustomizer() {
  			CustomizerService.INSTANCE.addOrUpdateCustomizer(originalCustomizer);
  		}
  		
  		public void clearAffineTransformCache() {
  			affineTransform = null;
  		}
  		
  		public AffineTransform getAffineTransform(ScriptEngine engine, BufferedImage buildPlatformImage, BufferedImage printImage) throws ScriptException {			
  			if (customizer != null && customizer.getAffineTransformSettings() != null) {
  				if (this.affineTransform == null || customizer.getAffineTransformSettings().getAffineTransformScriptCalculator() != null) {
  					this.affineTransform = customizer.createAffineTransform(this, engine, buildPlatformImage, printImage);
  				}
  			} else {
  				this.affineTransform = new AffineTransform();
  				affineTransform.translate(xResolution/2, yResolution/2);
  				affineTransform.translate(-printImage.getWidth()/2 , -printImage.getHeight()/2);
  			}
  			
  			return this.affineTransform;
  		}
  		
  		public int getRenderingSlice() {
  			if (customizer == null) {
  				return -1;
  			}
  			
  			if (renderingSlice == null) {
  				startSlice();
  			}
  			
  			return renderingSlice;
  		}
  		
  		public int startSlice(){
  			if (renderingSlice == null) {
  				if (customizer == null) {
  					return -1;
  				}
  				
  				renderingSlice = customizer.getNextSlice();
  				return renderingSlice;
  			}
  			
  			return renderingSlice++;
  		}
  		
  		public int completeRenderingSlice() {
  			int sliceJustRendered = customizer.getNextSlice();
  			customizer.setNextSlice(sliceJustRendered + 1);
  			customizer.setNextStep(PrinterStep.PerformPreSlice);
  			CustomizerService.INSTANCE.addOrUpdateCustomizer(customizer);
  			return sliceJustRendered;
  		}
  	}
  	
  	public Future<RenderingContext> startImageRendering(DataAid aid, Object imageIndexToBuild) {
  		aid.currentlyRenderingImage = createRenderer(aid, imageIndexToBuild);
  		aid.startSlice();
  		if (aid.currentlyRenderingImage == null) {
  			return null;
  		}
  		
  		return Main.GLOBAL_EXECUTOR.submit(aid.currentlyRenderingImage);
  	}
  	
  	
  	public abstract CurrentImageRenderer createRenderer(DataAid aid, Object imageIndexToBuild);
  
  	@Override
  	public Double getBuildAreaMM(PrintJob printJob) {
  		DataAid aid = getDataAid(printJob);
  		if (aid == null) {
  			return null;
  		}
  		
  		Double area = aid.cache.getCurrentArea();
  		if (area == null) {
  			return null;
  		}
  		
  		return aid.cache.getCurrentArea() / (aid.xPixelsPerMM * aid.yPixelsPerMM);
  	}
  	
  	@Override
  	public BufferedImage getCurrentImage(PrintJob printJob) {
  		return getCurrentImageFromCache(printJob);
  	}
  	
  	protected BufferedImage getCurrentImageFromCache(PrintJob printJob) {
  		DataAid data = getDataAid(printJob);
  		if (data == null) {
  			return null;
  		}
  		
  		ReentrantLock lock = data.cache.getCurrentLock();
  		lock.lock();
  		try {
  			BufferedImage currentImage = data.cache.getCurrentImage();
  			if (currentImage == null)
  				return null;
  			
  			return currentImage.getSubimage(0, 0, currentImage.getWidth(), currentImage.getHeight());
  		} finally {
  			lock.unlock();
  		}
  	}
  	
  	public final DataAid initializeJobCacheWithDataAid(PrintJob printJob) throws InappropriateDeviceException, JobManagerException {
  		DataAid aid = createDataAid(printJob);
  		printJob.setDataAid(aid);
  		
  		//Notify the client that the printJob has changed the current slice from -1 to 1 and totalSlices are properly set now as well.
  		NotificationManager.jobChanged(aid.printer, aid.printJob);
  		return aid;
  	}
  	
  	private void moveToNextPrinterStep(Customizer customizer, PrinterStep newState) {
  		customizer.setNextStep(newState);
  		CustomizerService.INSTANCE.addOrUpdateCustomizer(customizer);
  	}
  	
  	public void performPauseGCode(DataAid aid) throws InappropriateDeviceException, IOException {
  