package org.jactr.tools.itr; /* * default logging */ import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.antlr.runtime.tree.CommonTree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.model.IModel; import org.jactr.io.generator.CodeGeneratorFactory; import org.jactr.io.generator.ICodeGenerator; import org.jactr.io.resolver.ASTResolver; import org.jactr.tools.itr.ortho.ISlice; import org.jactr.tools.itr.ortho.ISliceListener; /** * saves models of terminal runs and restores them to the run directory so that * longitudinal runs can be bootstrapped from prior runs * * @author harrison */ public class LongitudinalParameterSetModifier extends ParameterSetModifier implements ISliceListener { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(LongitudinalParameterSetModifier.class); static private String TEMP_DIRECTORY = ".longitudinal"; private Map<String, String> _nameToFile = new TreeMap<String, String>(); private List<Collection<File>> _priorModels = new LinkedList<Collection<File>>(); /** * called by the parser.. should probably just be a parameter for simplicities * sake. * * @param modelName * @param modelFile */ public void associate(String modelName, String modelFile) { _nameToFile.put(modelName.toLowerCase(), modelFile); } /** * delete the models from working dir.. */ public void deleteModels() { /* * delete all models in working directory.. */ File root = new File(System.getProperty("user.dir")); for (String path : _nameToFile.values()) { File modelInRoot = new File(root, path); if (modelInRoot.exists()) delete(modelInRoot); } } /** * serializes the models to the temp directory * * @param models */ public void copyModels(Collection<IModel> models, long iteration) { ICodeGenerator generator = CodeGeneratorFactory.getCodeGenerator("jactr"); Collection<File> serialized = new ArrayList<File>(_nameToFile.size()); for (IModel model : models) try { String modelName = model.getName().toLowerCase(); String modelFile = _nameToFile.get(modelName); if (modelFile == null) { if (LOGGER.isWarnEnabled()) LOGGER.warn("Could not find binding for " + modelName); continue; } CommonTree modelDescriptor = ASTResolver.toAST(model, true); File root = new File(TEMP_DIRECTORY, String.format("%07d", iteration)); root.mkdirs(); File fp = new File(root, modelFile); if (!fp.getParentFile().equals(root)) fp.getParentFile().mkdirs(); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Copying %s to %s", model, fp)); PrintWriter pw = new PrintWriter(new FileWriter(fp)); for (StringBuilder line : generator.generate(modelDescriptor, true)) pw.println(line); pw.close(); /** * if the file is nested, store the parent, not the file itself */ if (!fp.getParentFile().equals(root)) serialized.add(fp.getParentFile()); else serialized.add(fp); } catch (Exception e) { LOGGER.error("Failed to generate file ", e); } if (serialized.size() == 0) if (LOGGER.isWarnEnabled()) LOGGER .warn("No models were saved, potential longitudinal configuration error. Check your modelName=modelFile bindings."); _priorModels.add(serialized); } @Override public void setParameter(CommonTree modelDescriptor, int parameterValueIndex) { super.setParameter(modelDescriptor, parameterValueIndex); } /** * is this the first slice in a longitudinal run? * * @param parameterValue * @return */ public boolean isFirstSlice(String parameterValue) { return getParameterValues().indexOf(parameterValue) == 0; } public boolean isLastSlice(String parameterValue) { List<String> parameterValues = getParameterValues(); return parameterValues.indexOf(parameterValue) == parameterValues.size() - 1; } /** * called before loading, we get a chance to copy models in.. */ public void startSlice(ISlice slice) { String stage = slice.getProperty(getParameterDisplayName()).toString(); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Starting slice %s", stage)); /* * let's just make sure we have a clean slate.. */ if (isFirstSlice(stage)) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "First longitudinal stage %s, cleaning (%s)", stage, slice)); deleteModels(); } } public void startIteration(ISlice slice, long iteration, Collection<IModel> models) { } public void stopIteration(ISlice slice, long iteration, Collection<IModel> models) { String stage = slice.getProperty(getParameterDisplayName()).toString(); if (!isFirstSlice(stage)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Cleaning working dir"); deleteModels(); } /* * and copy */ copyModels(models, iteration - slice.getFirstIteration()); /* * we copy in the models for the next iteration within this slice.. if this * is the last iteration, we'll leave the coping for the next slice to * stopSlice() */ if (!isFirstSlice(stage) && slice.getLastIteration() != iteration) { long offsetIteration = iteration - slice.getFirstIteration() + 1; if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "Need to copy files from %07d for stage %s (%s)", offsetIteration, stage, slice)); moveFiles(offsetIteration); } } private void moveFiles(long offsetIteration) { Collection<File> files = _priorModels.remove(0); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Moving %s to root for iteration %d", files, offsetIteration)); for (File file : files) { File dest = new File(file.getName()); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Moving %s to %s", file, dest)); if (!file.renameTo(dest)) LOGGER.error(String.format("Failed to move %s to %s", file, dest)); } } public void stopSlice(ISlice slice) { String stage = slice.getProperty(getParameterDisplayName()).toString(); if (isLastSlice(stage)) { /* * clean out the temp dir.. */ if (LOGGER.isDebugEnabled()) LOGGER.debug("Was final stage slice, deleting temp"); delete(new File(TEMP_DIRECTORY)); } else { /* * we just finished the last iteration of a slice, but there are more to * come and since we can't get in before the models are loaded, we move * the files here, w/ an index of 0 */ if (LOGGER.isDebugEnabled()) LOGGER .debug("slice completed, moving longitudinal models for start of next stage"); moveFiles(0); } } private void delete(File root) { File[] children = root.listFiles(); if (children != null) for (File child : children) if (!child.isDirectory()) try { if (LOGGER.isDebugEnabled()) LOGGER.debug("Deleting " + child); child.delete(); } catch (Exception e) { LOGGER.error("Could not delete " + child, e); } else delete(child); root.delete(); } }