/*
* Created on Mar 13, 2007 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu
* (jactr.org) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version. This library is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jactr.tools.io;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Function;
import org.antlr.runtime.tree.CommonTree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.concurrent.ExecutorServices;
import org.jactr.core.model.IModel;
import org.jactr.core.model.event.ModelEvent;
import org.jactr.core.model.event.ModelListenerAdaptor;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.utils.parameter.IParameterized;
import org.jactr.core.utils.parameter.ParameterHandler;
import org.jactr.instrument.IInstrument;
import org.jactr.io.generator.CodeGeneratorFactory;
import org.jactr.io.generator.ICodeGenerator;
import org.jactr.io.resolver.ASTResolver;
/**
* A simple instrument that saves the state of the model before it starts and
* after it stops.
*
* @author developer
*/
public class ModelRecorder implements IInstrument, IParameterized
{
/**
* logger definition
*/
static private final Log LOGGER = LogFactory
.getLog(ModelRecorder.class);
static final public String SAVE_AS_PARAM = "SaveAsExtension";
static final public String START_PARAM = "StartDirectory";
static final public String STOP_PARAM = "StopDirectory";
static final public String TRIM_CONTRIBUTIONS_PARAM = "TrimModuleContributions";
static final public String AST_GENERATOR = "ASTGenerator";
private String _saveAsExtension = "jactr";
private String _startDirectory = "start";
private String _stopDirectory = "stop";
private boolean _trimModuleContributions = true;
static final private Function<IModel, CommonTree> _defaultASTGenerator = (m) -> {
return ASTResolver
.toAST(m,
true);
};
private Function<IModel, CommonTree> _astGenerator = _defaultASTGenerator;
/**
* IInstruments should always have a zero arg constructor
*/
public ModelRecorder()
{
}
/**
* @see org.jactr.instrument.IInstrument#initialize()
*/
public void initialize()
{
// NoOp
}
/**
* @see org.jactr.instrument.IInstrument#install(org.jactr.core.model.IModel)
*/
public void install(IModel model)
{
model.addListener(new ModelListenerAdaptor() {
@Override
public void modelStarted(ModelEvent me)
{
// saveModel(me.getSource(), _startDirectory, _saveAsExtension,
// _trimModuleContributions);
save(me.getSource(), _startDirectory);
}
@Override
public void modelStopped(ModelEvent me)
{
// saveModel(me.getSource(), _stopDirectory, _saveAsExtension,
// _trimModuleContributions);
save(me.getSource(), _stopDirectory);
}
}, ExecutorServices.INLINE_EXECUTOR);
}
/**
* @see org.jactr.instrument.IInstrument#uninstall(org.jactr.core.model.IModel)
*/
public void uninstall(IModel model)
{
}
protected CommonTree generateAST(IModel model)
{
CommonTree modelDescriptor = _astGenerator.apply(model);
return modelDescriptor;
}
protected void save(IModel model, String directory)
{
saveModel(model, generateAST(model), directory, _saveAsExtension,
_trimModuleContributions);
}
/**
* @see org.jactr.core.utils.parameter.IParameterized#getParameter(java.lang.String)
*/
public String getParameter(String key)
{
if (SAVE_AS_PARAM.equalsIgnoreCase(key)) return _saveAsExtension;
if (START_PARAM.equalsIgnoreCase(key)) return _startDirectory;
if (STOP_PARAM.equalsIgnoreCase(key)) return _stopDirectory;
if (TRIM_CONTRIBUTIONS_PARAM.equalsIgnoreCase(key))
return "" + _trimModuleContributions;
if (AST_GENERATOR.equalsIgnoreCase(key))
return _astGenerator == _defaultASTGenerator ? "default" : _astGenerator
.getClass().getName();
return null;
}
/**
* @see org.jactr.core.utils.parameter.IParameterized#getPossibleParameters()
*/
public Collection<String> getPossibleParameters()
{
return Arrays.asList(new String[] { SAVE_AS_PARAM, START_PARAM, STOP_PARAM,
TRIM_CONTRIBUTIONS_PARAM, AST_GENERATOR });
}
/**
* @see org.jactr.core.utils.parameter.IParameterized#getSetableParameters()
*/
public Collection<String> getSetableParameters()
{
return getPossibleParameters();
}
/**
* this chunk of code will permit the ModelRecorder to be configured when the
* environment.xml file is loaded
*
* @see org.jactr.core.utils.parameter.IParameterized#setParameter(java.lang.String,
* java.lang.String)
*/
public void setParameter(String key, String value)
{
if (SAVE_AS_PARAM.equalsIgnoreCase(key))
_saveAsExtension = value;
else if (START_PARAM.equalsIgnoreCase(key))
_startDirectory = value;
else if (STOP_PARAM.equalsIgnoreCase(key))
_stopDirectory = value;
else if (TRIM_CONTRIBUTIONS_PARAM.equalsIgnoreCase(key))
_trimModuleContributions = Boolean.parseBoolean(value);
else if (AST_GENERATOR.equalsIgnoreCase(key))
{
value = value.trim();
if (value.equalsIgnoreCase("default") || value.equals(""))
_astGenerator = _defaultASTGenerator;
else
try
{
_astGenerator = (Function<IModel, CommonTree>) ParameterHandler
.classInstance().coerce(value).newInstance();
}
catch (Exception e)
{
LOGGER.error(
String.format("Failed to set ast generator to %s ", value), e);
_astGenerator = _defaultASTGenerator;
}
}
else if (LOGGER.isWarnEnabled())
LOGGER.warn("No clue what to do with " + key + "=" + value);
}
static public void saveModel(IModel model, String directory,
String extension, boolean trim)
{
saveModel(model, ASTResolver.toAST(model, true), directory, extension, trim);
}
/*
* here is where we actually save the model
*/
static public void saveModel(IModel model, CommonTree modelDescriptor,
String directory, String extension, boolean trim)
{
ICodeGenerator generator = CodeGeneratorFactory.getCodeGenerator(extension);
if (generator == null)
{
if (LOGGER.isWarnEnabled())
LOGGER.warn("Could not find a code generator for " + extension);
return;
}
String destination = null;
try
{
/*
* ok, now let's create the directory
*/
File root = new File(ACTRRuntime.getRuntime().getWorkingDirectory(),
directory);
destination = root.getCanonicalPath();
if (!root.exists()) root.mkdirs();
File toBeWritten = new File(root, model.getName() + "." + extension);
destination = toBeWritten.getCanonicalPath();
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(
toBeWritten)));
/*
* now before we dump we should make sure that the buffer contents are
* written
*/
/*
* we dump the full model
*/
for (StringBuilder line : generator.generate(modelDescriptor, trim))
pw.println(line.toString());
pw.flush();
pw.close();
}
catch (Exception ioe)
{
LOGGER
.error("Could not write " + model + " to " + destination + " ", ioe);
}
}
}