/*
* Below is the copyright agreement for the Ptolemy II system.
*
* Copyright (c) 2009 The Regents of the University of California. All
* rights reserved.
*
* Permission is hereby granted, without written agreement and without license
* or royalty fees, to use, copy, modify, and distribute this software and its
* documentation for any purpose, provided that the above copyright notice and
* the following two paragraphs appear in all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
* "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
package ptolemy.data.properties;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Constructor;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import ptolemy.data.BooleanToken;
import ptolemy.data.expr.Parameter;
import ptolemy.data.expr.StringParameter;
import ptolemy.data.properties.lattice.PropertyConstraintSolver;
import ptolemy.data.type.BaseType;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.attributes.URIAttribute;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.InternalErrorException;
import ptolemy.kernel.util.KernelException;
import ptolemy.kernel.util.Location;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.NamedObj;
import ptolemy.util.FileUtilities;
/**
*
* @author Man-Kit Leung
* @version $Id$
* @since Ptolemy II 7.1
* @Pt.ProposedRating Red (mankit)
* @Pt.AcceptedRating Red (mankit)
*/
public class AnalyzerAttribute extends Attribute {
/**
* Construct an AnalyzerAttribute with the specified container and name.
* @param container The specified container.
* @param name The specified name.
* @exception IllegalActionException If the attribute is not of an
* acceptable class for the container, or if the name contains a period.
* @exception NameDuplicationException If the name coincides with an
* attribute already in the container.
*/
public AnalyzerAttribute(NamedObj container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
property = new StringParameter(container, "property");
action = new StringParameter(container, "action");
_addChoices();
showProperty = new Parameter(container, "showProperty");
showProperty.setTypeEquals(BaseType.BOOLEAN);
showProperty.setExpression("true");
highlight = new Parameter(container, "highlight");
highlight.setTypeEquals(BaseType.BOOLEAN);
highlight.setExpression("true");
logMode = new Parameter(container, "logMode");
logMode.setTypeEquals(BaseType.BOOLEAN);
logMode.setExpression("true");
overwriteConstraint = new Parameter(container, "overwriteConstraint");
overwriteConstraint.setTypeEquals(BaseType.BOOLEAN);
overwriteConstraint.setExpression("false");
overwriteDependentProperties = new Parameter(container,
"overwriteDependentProperties");
overwriteDependentProperties.setTypeEquals(BaseType.BOOLEAN);
overwriteDependentProperties.setExpression("false");
}
///////////////////////////////////////////////////////////////////
//// ports and parameters ////
public Parameter action;
public Parameter highlight;
public Parameter logMode;
public Parameter overwriteConstraint;
public Parameter overwriteDependentProperties;
/**
* The property to analyze.
*/
public Parameter property;
/**
* Whether to display the annotated property or not.
*/
public Parameter showProperty;
///////////////////////////////////////////////////////////////////
//// public methods ////
public String analyze(CompositeEntity entity) throws IllegalActionException {
String errorString = "";
String propertyValue = property.getExpression();
if (propertyValue.equals("Clear All")) {
try {
PropertyRemover remover = new PropertyRemover(entity,
"ModelAnalyzerClearAll");
remover.removeProperties(entity);
} catch (NameDuplicationException e) {
assert false;
}
} else {
String actionValue = action.getExpression();
PropertySolver chosenSolver = null;
try {
URIAttribute attribute = (URIAttribute) entity.getAttribute(
"_uri", URIAttribute.class);
if (attribute == null) {
attribute = new URIAttribute(entity, "_uri");
}
if (attribute.getURI() == null) {
URI uri = getModelURI(getName() + "_" + entity.getName());
attribute.setURI(uri);
}
List solversInModel = entity
.attributeList(PropertySolver.class);
if (solversInModel.size() > 0) {
try {
chosenSolver = ((PropertySolver) solversInModel.get(0))
.findSolver(propertyValue);
} catch (PropertyResolutionException ex) {
}
}
if (chosenSolver == null) {
chosenSolver = instantiateSolver(entity, propertyValue);
}
for (String solverName : chosenSolver.getDependentSolvers()) {
try {
chosenSolver.findSolver(solverName);
} catch (PropertyResolutionException ex) {
instantiateSolver(entity, solverName);
}
}
if (chosenSolver instanceof PropertyConstraintSolver) {
((PropertyConstraintSolver) chosenSolver)
.setLogMode(logMode.getToken() == BooleanToken.TRUE);
}
String previousAction = chosenSolver.setAction(actionValue);
chosenSolver.invokeSolver(getContainer());
chosenSolver.setAction(previousAction);
} catch (PropertyFailedRegressionTestException ex) {
errorString = KernelException.generateMessage(entity, null, ex,
"****Failed: Property regression test failed.")
+ "\n\n";
} catch (KernelException ex) {
errorString = KernelException.generateMessage(entity, null, ex,
"****Failed: Property regression test failed.")
+ "\n\n";
} catch (URISyntaxException ex) {
errorString = KernelException.generateMessage(entity, null, ex,
"****Failed: Property regression test failed.")
+ "\n\n";
} catch (Exception ex) {
errorString = KernelException.generateMessage(entity, null, ex,
"****Failed: Property regression test failed.")
+ "\n\n";
} finally {
if (chosenSolver != null) {
chosenSolver.resetAll();
}
// _removeSolvers(entity);
}
}
return errorString;
}
public URI getModelURI(String modelName) throws URISyntaxException {
URI uri = URIAttribute.getModelURI(this);
String path = uri.getPath();
int pos = path.lastIndexOf('/');
if (pos >= 0) {
path = path.substring(0, pos + 1) + modelName + ".xml";
} else {
path += "/" + modelName + ".xml";
}
return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri
.getPort(), path, uri.getQuery(), uri.getFragment());
}
/**
* Given a dot separated packagename, return a list of all the packages in
* the given path and below. The list of solver classes is also updated.
* @param path The dot separated package name, such as
* "ptolemy.data.properties.configuredSolvers"
* @return a List of packages that are found.
*/
public List<Class> getListOfSolverClass(String path) {
List<Class> solvers = new LinkedList<Class>();
String directoryPath = "$CLASSPATH/" + path.replace(".", "/");
try {
URI directoryURI = new URI(FileUtilities.nameToURL(directoryPath,
null, null).toExternalForm().replaceAll(" ", "%20"));
File directory = null;
try {
try {
directory = new File(directoryURI);
} catch (Throwable throwable) {
throw new InternalErrorException(this, throwable,
"Failed to find directories in the URI: \""
+ directoryURI + "\"");
}
ClassFileOrDirectoryNameFilter filter = new ClassFileOrDirectoryNameFilter();
File[] classFiles = directory.listFiles(filter);
if (classFiles == null) {
throw new InternalErrorException(this, null,
"Failed to find directories in \"" + directoryPath
+ "\"");
} else {
for (File element : classFiles) {
String className = element.getName();
if (element.isDirectory()) {
if (!className.equals("CVS")
&& !className.equals(".svn")) {
// Search sub-folder.
solvers.addAll(getListOfSolverClass(path + "."
+ className));
}
} else {
try {
// only consider class files
if (className.endsWith(".class")) {
solvers.add(Class.forName(path
+ "."
+ className.substring(0, className
.length() - 6)));
}
} catch (ClassNotFoundException e) {
assert false;
}
}
}
}
} catch (Throwable throwable) {
try {
if (!directoryURI.toString().startsWith("jar:")) {
throw throwable;
} else {
new LinkedList<String>();
URL jarURL = directoryURI.toURL();
JarURLConnection connection = (JarURLConnection) jarURL
.openConnection();
String jarEntryName = connection.getEntryName();
if (!jarEntryName.endsWith("/")) {
jarEntryName = jarEntryName + "/";
}
JarFile jarFile = connection.getJarFile();
Enumeration entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = (JarEntry) entries.nextElement();
String name = entry.getName();
if (name.startsWith(jarEntryName)
&& name.endsWith(".class")
&& !entry.isDirectory()) {
String className = name.replace("/", ".")
.substring(0, name.length() - 6);
try {
solvers.add(Class.forName(className));
} catch (ClassNotFoundException ex) {
throw new InternalErrorException(this, ex,
"Can't find class " + className);
}
}
}
}
} catch (Throwable throwable2) {
throwable2.printStackTrace();
throw new IllegalActionException(this, throwable,
"Failed to process " + directoryURI);
}
}
} catch (Throwable ex) {
throw new InternalErrorException(this, ex,
"Failed to find classes in \"" + directoryPath + "\"");
}
return solvers;
}
public PropertySolver instantiateSolver(CompositeEntity entity,
String className) {
for (Class solver : _solvers) {
if (className.equals(solver.getSimpleName())) {
try {
Constructor constructor = solver.getConstructor(
NamedObj.class, String.class);
PropertySolver solverObject = (PropertySolver) constructor
.newInstance(entity, "ModelAnalyzer_"
+ solver.getSimpleName());
new Location(solverObject, "_location");
return solverObject;
} catch (Throwable ex) {
assert false;
}
break;
}
}
return null;
}
public void setContainer(NamedObj container) throws IllegalActionException,
NameDuplicationException {
super.setContainer(container);
_moveParameter(action);
_moveParameter(property);
_moveParameter(showProperty);
_moveParameter(highlight);
_moveParameter(logMode);
_moveParameter(overwriteConstraint);
_moveParameter(overwriteDependentProperties);
}
///////////////////////////////////////////////////////////////////
//// private methods ////
private void _addChoices() throws IllegalActionException {
_solvers.addAll(getListOfSolverClass("ptolemy.data.properties.configuredSolvers"));
if (_solvers.size() > 0) {
property.setExpression(_solvers.get(0).getSimpleName());
}
for (Class solver : _solvers) {
property.addChoice(solver.getSimpleName());
}
property.addChoice("Clear All");
PropertySolver._addActions(action);
}
private void _moveParameter(Parameter parameter)
throws IllegalActionException, NameDuplicationException {
if (parameter != null) {
if (parameter.getContainer() != getContainer()) {
parameter.setContainer(getContainer());
}
}
}
///////////////////////////////////////////////////////////////////
//// private variables ////
private final List<Class> _solvers = new LinkedList<Class>();
///////////////////////////////////////////////////////////////////
//// inner classes ////
/**
* Look for directories that do are not CVS or .svn.
*/
static class ClassFileOrDirectoryNameFilter implements FilenameFilter {
// FindBugs suggests making this class static so as to decrease
// the size of instances and avoid dangling references.
/**
* Return true if the specified file names a directory that is not named
* "CVS" or ".svn".
* @param directory the directory in which the potential directory was
* found.
* @param name the name of the directory or file.
* @return true if the file is a directory that contains a file called
* configuration.xml
*/
public boolean accept(File directory, String name) {
try {
File file = new File(directory, name);
if (file.isDirectory()
&& (file.getName().equals("CVS") || file.getName()
.equals(".svn"))) {
return false;
}
if (!file.isDirectory() && file.getName().endsWith(".class")) {
return true;
}
if (file.isDirectory()) {
return true;
}
} catch (Exception ex) {
return false;
}
return false;
}
}
}