blob: 2af4057018c64b9198b96cdb75b4541cbc025de6 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2014 Obeo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
*
* </copyright>
*/
package org.eclipse.ocl.examples.standalone.validity;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.examples.emf.validation.validity.RootNode;
import org.eclipse.ocl.examples.emf.validation.validity.export.IValidityExporter;
import org.eclipse.ocl.examples.emf.validation.validity.export.IValidityExporterDescriptor;
import org.eclipse.ocl.examples.emf.validation.validity.export.ValidityExporterRegistry;
import org.eclipse.ocl.examples.pivot.validation.PivotEObjectValidator.ValidationAdapter;
import org.eclipse.ocl.examples.standalone.StandaloneApplication;
import org.eclipse.ocl.examples.standalone.StandaloneCommand;
import org.eclipse.ocl.examples.standalone.StandaloneResponse;
import org.eclipse.ocl.examples.standalone.messages.StandaloneMessages;
import org.eclipse.ocl.examples.xtext.completeocl.ui.commands.LoadCompleteOCLResourceHandler.Helper;
/**
* The ValidateCommand provides model validation.
*/
public class ValidateCommand extends StandaloneCommand
{
private static final Logger logger = Logger.getLogger(ValidateCommand.class);
protected static final class ExporterComparator implements Comparator<IValidityExporterDescriptor>
{
public static final @NonNull ExporterComparator INSTANCE = new ExporterComparator();
@Override
public int compare(IValidityExporterDescriptor o1, IValidityExporterDescriptor o2) {
String n1 = o1.getExporterType();
String n2 = o2.getExporterType();
return n1.compareTo(n2);
}
}
/**
* An optional argument to specify which exporter should be used. By
* default, the �txt� exporter will be used, exporting a textual report of
* the validation.
*/
public static class ExporterToken extends StringToken
{
public ExporterToken() {
super("-exporter", StandaloneMessages.ValidateCommand_Exporter_Help);
}
public boolean check(@NonNull List<String> strings) {
return getExporter(strings) != null;
}
public @Nullable String getArgsHelp() {
List<IValidityExporterDescriptor> exporters = new ArrayList<IValidityExporterDescriptor>(ValidityExporterRegistry.INSTANCE.getRegisteredExtensions());
Collections.sort(exporters, ExporterComparator.INSTANCE);
StringBuilder s = new StringBuilder();
for (IValidityExporterDescriptor exporter : exporters) {
if (s.length() > 0) {
s.append("|");
}
s.append(exporter.getExporterType());
}
return s.toString();
}
private @Nullable IValidityExporter getExporter(@NonNull List<String> strings) {
if (strings.size() <= 0) {
return null;
}
String string = strings.get(0);
return ValidityExporterRegistry.INSTANCE.getExporter(string);
}
public @Nullable IValidityExporter getExporter(@NonNull Map<CommandToken, List<String>> token2strings) {
List<String> strings = token2strings.get(this);
if (strings == null) {
return null;
}
return getExporter(strings);
}
/**
* Gets the validation exporter corresponding to the argument read after the
* <b>-report</b> argument.
*
* @return The validation exporter.
*/
// public AbstractExporter getExporter() {
// return exporter;
// }
}
/**
* A mandatory argument key of the model file path. This argument key must
* be followed by the model file path.
*/
public static class ModelToken extends StringToken
{
public ModelToken() {
super("-model", StandaloneMessages.ValidateCommand_Model_Help);
}
public boolean check(@NonNull List<String> strings) {
return getModelFileName(strings) != null;
}
public @Nullable String getArgsHelp() {
return "<file-name>";
}
private @Nullable String getModelFileName(@NonNull List<String> strings) {
if (strings.size() <= 0) {
return null;
}
String string = strings.get(0);
return getCheckedFileName(string);
}
public @Nullable String getModelFileName(@NonNull Map<CommandToken, List<String>> token2strings) {
List<String> strings = token2strings.get(this);
if (strings == null) {
return null;
}
return getModelFileName(strings);
}
/**
* Gets the absolute path to the model file deduced from the value specified
* after the argument <b>-model</b>.
*
* @return the model path as a String.
*/
// public IPath getModelFilePath() {
// return modelPath;
// }
}
/**
* An optional argument to define the output file path. The exporter will
* create results within that target file.
*/
public static class OutputToken extends StringToken
{
public OutputToken() {
super("-output", StandaloneMessages.ValidateCommand_Output_Help);
}
public boolean check(@NonNull List<String> strings) {
return getOutputFile(strings) != null;
}
public @Nullable String getArgsHelp() {
return "<file-name>";
}
private @Nullable File getOutputFile(@NonNull List<String> strings) {
if (strings.size() <= 0) {
return null;
}
String string = strings.get(0);
try {
File file = new File(string).getCanonicalFile();
if (file.exists()) {
if (file.isFile()) {
file.delete();
} else {
logger.error(StandaloneMessages.OCLArgumentAnalyzer_OutputFile
+ file.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_NotFile);
}
}
if (!file.exists()) {
// outputFilePath = new Path(file.getAbsolutePath());
// outputFile = file;
File outputFolder = file.getParentFile();
if (!outputFolder.exists()) {
logger.error(StandaloneMessages.OCLArgumentAnalyzer_OutputDir
+ outputFolder.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_NotExist);
} else {
return file;
}
}
} catch (IOException e) {
logger.error(e.getMessage());
}
return null;
}
public File getOutputFile(@NonNull Map<CommandToken, List<String>> token2strings) {
List<String> strings = token2strings.get(this);
if (strings == null) {
return null;
}
return getOutputFile(strings);
}
}
/**
* A mandatory argument used to define the paths to the OCL documents
* containing the constraints to evaluate. Users can specify one or several
* OCL Documents paths in the command line, separated with a whitespace. A
* text file containing a list of OCL Documents paths can be used instead,
* in which case all OCL constraints defined in all of these documents will
* be evaluated sequentially.
*/
public static class RulesToken extends CommandToken
{
/** Possible "text" extension file for the "-rules" argument entry. */
private static final Object TEXT_FILE_EXTENSION = "txt"; //$NON-NLS-1$
/** Possible "ocl" extension file for the "-rules" argument entry. */
private static final Object OCL_FILE_EXTENSION = "ocl"; //$NON-NLS-1$
public RulesToken() {
super("-rules", StandaloneMessages.ValidateCommand_Rules_Help);
}
public boolean check(@NonNull List<String> strings) {
if (strings.size() <= 0) {
// return false; -- all files might be ignored
}
for (String string : strings) {
String checkedName = getCheckedFileName(string);
if (checkedName == null) {
return false;
}
}
return true;
}
public int parseArgument(@NonNull List<String> strings, @NonNull String[] arguments, int i) {
if (i < arguments.length){
String argument = arguments[i++];
checkOclFile(strings, argument);
return i;
}
else {
logger.error("No argument for '" + name + "'");
return -1;
}
}
/**
* Checks consistency of the ocl file passed to the command line.
*
* @param argument
* is the path to the relative/absolute path to the resource
* @return <code>true</code> if the model exists and is a file,
* <code>false</code> otherwise.
*/
private void checkOclFile(@NonNull List<String> strings, @NonNull String argument) {
if (argument.startsWith("file:")) {
argument = argument.substring(5);
}
if (isWindows() && argument.startsWith("/")) {
argument = argument.substring(1);
}
boolean ignored = false;
try {
File file = new File(argument).getCanonicalFile();
IPath path = new Path(file.getCanonicalPath());
// a txt file may contain relative or absolute path to a set of OCL
// files.
if (TEXT_FILE_EXTENSION.equals(path.getFileExtension().toLowerCase())) {
extractOCLUris(strings, file);
} else if (OCL_FILE_EXTENSION.equals(path.getFileExtension().toLowerCase())) {
if (!file.exists()) {
logger.warn(StandaloneMessages.OCLArgumentAnalyzer_OCLResource
+ file.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_NotExist);
ignored = true;
} else if (!file.isFile()) {
logger.warn(StandaloneMessages.OCLArgumentAnalyzer_OCLResource
+ file.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_NotFile);
ignored = true;
} else if (!file.canRead()) {
logger.warn(StandaloneMessages.OCLArgumentAnalyzer_OCLResource
+ file.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_CannotBeRead);
ignored = true;
} else {
strings.add(file.getAbsolutePath());
}
} else {
logger.warn(StandaloneMessages.OCLArgumentAnalyzer_FileExt
+ path.lastSegment()
+ StandaloneMessages.OCLArgumentAnalyzer_ExtensionPb);
ignored = true;
}
if (ignored) {
logger.warn(StandaloneMessages.OCLArgumentAnalyzer_OCLFile
+ file.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_ignored);
// } else {
// logger.info(StandaloneMessages.OCLArgumentAnalyzer_OCLFile
// + file.getAbsolutePath()
// + StandaloneMessages.OCLArgumentAnalyzer_found);
}
} catch (IOException e) {
logger.warn(e.getMessage());
}
}
/**
* Extracts information contained in the text file.
*
* @param txtFile
* The file containing relative path to OCL files.
*/
private void extractOCLUris(@NonNull List<String> strings, File txtFile) {
BufferedReader reader;
try {
reader = new BufferedReader(new FileReader(txtFile));
String line = reader.readLine();
while (line != null) {
File child = new File(txtFile.getParentFile(), line);
checkOclFile(strings, child.toString());
line = reader.readLine();
}
reader.close();
} catch (FileNotFoundException e) {
logger.error(MessageFormat
.format(StandaloneMessages.OCLArgumentAnalyzer_OCLFileNotFound,
txtFile.getAbsolutePath()));
} catch (IOException e) {
logger.warn(e.getMessage());
}
}
public @Nullable String getArgsHelp() {
return "<file-name>";
}
/**
* Gets the collection of OCL resources deduced from values specified after
* the <b>-rule</b> argument.
*
* @return A List of OCL Uris
*/
public @NonNull List<String> getOCLFileNames(@NonNull Map<CommandToken, List<String>> token2strings) {
List<String> strings = token2strings.get(this);
if (strings == null) {
return Collections.emptyList();
}
return strings;
}
}
/**
* An optional argument used if the user wishes to run all constraints or to
* only run the OCL, Java or UML constraints validation. Otherwise, all
* constraints will be checked against the input model.
*/
public static class UsingToken extends StringToken
{
/** "-using" argument value to run the all constraints (ocl, java and uml). */
private static final String ALL_LOCATORS = "all"; //$NON-NLS-1$
/** "-using" argument value to additionally run the OCL constraints. */
private static final String OCL_LOCATOR = "ocl"; //$NON-NLS-1$
/** "-using" argument value to additionally run the Java constraints. */
private static final String JAVA_LOCATOR = "java"; //$NON-NLS-1$
/** "-using" argument value to additionally run the UML constraints. */
private static final String UML_LOCATOR = "uml"; //$NON-NLS-1$
public UsingToken() {
super("-using", StandaloneMessages.ValidateCommand_Using_Help);
}
public boolean check(@NonNull List<String> locators) {
for (String locator : locators) {
if (!ALL_LOCATORS.equals(locator) && !JAVA_LOCATOR.equals(locator) && !OCL_LOCATOR.equals(locator) && !UML_LOCATOR.equals(locator)) {
logger.error("Unknown locator '" + locator + "'");
return false;
}
}
return true;
}
public boolean doRunJavaConstraints(@NonNull Map<CommandToken, List<String>> token2strings) {
List<String> strings = token2strings.get(this);
return (strings == null) || strings.contains(JAVA_LOCATOR) || strings.contains(ALL_LOCATORS);
}
public boolean doRunOCLConstraints(@NonNull Map<CommandToken, List<String>> token2strings) {
List<String> strings = token2strings.get(this);
return (strings == null) || strings.contains(OCL_LOCATOR) || strings.contains(ALL_LOCATORS);
}
public boolean doRunUMLConstraints(@NonNull Map<CommandToken, List<String>> token2strings) {
List<String> strings = token2strings.get(this);
return (strings == null) || strings.contains(UML_LOCATOR) || strings.contains(ALL_LOCATORS);
}
public @Nullable String getArgsHelp() {
return ALL_LOCATORS + "|" + JAVA_LOCATOR + "|" + OCL_LOCATOR + "|" + UML_LOCATOR;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public int parseArgument(@NonNull List<String> strings, @NonNull String[] arguments, int i) {
if (i < arguments.length){
String argument = arguments[i++];
String[] locators = argument.split(",");
for (String locator : locators) {
if (!ALL_LOCATORS.equals(locator) && !JAVA_LOCATOR.equals(locator) && !OCL_LOCATOR.equals(locator) && !UML_LOCATOR.equals(locator)) {
logger.error("Unknown locator '" + locator + "'");
return -1;
}
}
for (String locator : locators) {
strings.add(locator);
}
return i;
}
else {
logger.error("No argument for '" + name + "'");
return -1;
}
}
}
protected static String getCheckedFileName(@NonNull String string) {
if (string.startsWith("file:")) {
string = string.substring(5);
}
if (isWindows() && string.startsWith("/")) {
string = string.substring(1);
}
try {
File file = new File(string).getCanonicalFile();
if (!file.exists()) {
logger.error(StandaloneMessages.OCLArgumentAnalyzer_ModelFile
+ file.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_NotExist);
} else if (!file.isFile()) {
logger.error(StandaloneMessages.OCLArgumentAnalyzer_ModelFile
+ file.getAbsolutePath()
+ StandaloneMessages.OCLArgumentAnalyzer_NotFile);
} else {
@SuppressWarnings("unused")
IPath modelPath = new Path(file.getAbsolutePath());
// logger.info(StandaloneMessages.OCLArgumentAnalyzer_ModelFile
// + file.getAbsolutePath()
// + StandaloneMessages.OCLArgumentAnalyzer_found);
return string;//modelPath;
}
} catch (IOException e) {
logger.error(e.getMessage());
}
return null;
}
/**
* Gets an URI from a file Path.
*
* @param filePath
* the file path.
* @return an URI from the path.
*/
private static URI getFileUri(@NonNull String fileName) {
final URI fileUri;
File file;
try {
file = new File(fileName).getCanonicalFile(); // FIXME is this necessary
IPath filePath = new Path(file.getAbsolutePath());
// IPath filePath = new Path(fileName);
if (isRelativePath(filePath)) {
fileUri = URI.createPlatformResourceURI(filePath.toString(), true);
} else {
fileUri = URI.createFileURI(filePath.toString());
}
return fileUri;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
/**
* Checks if the path is relative or absolute.
*
* @param path
* a file path.
* @return true if the path is relative, false otherwise.
*/
private static boolean isRelativePath(IPath path) {
try {
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
return resource != null && resource.exists();
} catch (IllegalStateException exception) {
return false;
}
}
public static boolean isWindows() {
String os = System.getProperty("os.name");
return (os != null) && os.startsWith("Windows");
}
public final @NonNull ExporterToken exporterToken = new ExporterToken();
public final @NonNull ModelToken modelToken = new ModelToken();
public final @NonNull OutputToken outputToken = new OutputToken();
public final @NonNull RulesToken rulesToken = new RulesToken();
public final @NonNull UsingToken usingToken = new UsingToken();
public ValidateCommand(@NonNull StandaloneApplication standaloneApplication) {
super(standaloneApplication, "validate", StandaloneMessages.ValidateCommand_Help);
modelToken.setIsRequired();
rulesToken.setIsRequired();
addToken(modelToken);
addToken(rulesToken);
addToken(outputToken);
addToken(exporterToken);
addToken(usingToken);
}
public @NonNull StandaloneResponse execute(@NonNull Map<CommandToken, List<String>> token2strings) {
standaloneApplication.doCompleteOCLSetup();
String modelFileName = modelToken.getModelFileName(token2strings);
List<String> oclFileNames = rulesToken.getOCLFileNames(token2strings);
URI modelURI = getFileUri(modelFileName);
// Load model resource
Resource modelResource = standaloneApplication.loadModelFile(modelURI);
if (modelResource == null) {
logger.error(MessageFormat.format(StandaloneMessages.OCLValidatorApplication_ModelLoadProblem, modelFileName));
return StandaloneResponse.FAIL;
}
if (!processResources(modelFileName, oclFileNames)) {
logger.error(StandaloneMessages.OCLValidatorApplication_Aborted);
return StandaloneResponse.FAIL;
}
if (ValidationAdapter.findAdapter(standaloneApplication.getResourceSet()) == null) {
logger.error(StandaloneMessages.OCLValidatorApplication_Aborted);
return StandaloneResponse.FAIL;
}
StandaloneValidityManager validityManager = initiateValidityManager(standaloneApplication.getResourceSet(), token2strings);
if (validityManager != null) {
// run the validation
validate(validityManager);
// export results
File outputFile = outputToken.getOutputFile(token2strings);
exportValidationResults(validityManager.getRootNode(), modelResource, outputFile, token2strings);
// try {
// exportValidationResults(getOutputWriter(), validityManager.getRootNode());
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
return StandaloneResponse.OK;
}
/**
* Exports Validation results.
*
* @param rootNode
* the validity model rootNode.
* @param outputPath
* the exported file path.
*/
private void exportValidationResults(@NonNull RootNode rootNode, @NonNull Resource modelResource, @Nullable File outputFile, @NonNull Map<CommandToken, List<String>> token2strings) {
final IValidityExporter selectedExporter = exporterToken.getExporter(token2strings);
if (selectedExporter != null && modelResource != null && rootNode != null) {
// logger.info(StandaloneMessages.OCLValidatorApplication_ExportStarting);
Appendable s = null;
try {
s = outputFile != null ? new FileWriter(outputFile) : System.out;
selectedExporter.export(s, modelResource, rootNode, outputFile != null ? outputFile.toString() : null);
} catch (IOException e) {
logger.error(StandaloneMessages.OCLValidatorApplication_ExportProblem, e);
} finally {
if (s != System.out) {
try {
((FileWriter)s).close();
} catch (IOException e) {}
}
}
// logger.info(StandaloneMessages.OCLValidatorApplication_ExportedFileGenerated);
// } else {
// logger.info(StandaloneMessages.OCLValidatorApplication_ExportProblem);
}
}
/**
* Initiates the validity manager using the resourceSet.
*
* @param resourceSet
* the resource set.
*/
private @NonNull StandaloneValidityManager initiateValidityManager(@NonNull ResourceSet resourceSet, @NonNull Map<CommandToken, List<String>> token2strings) {
StandaloneValidityManager validityManager = new StandaloneValidityManager();
validityManager.setRunJavaConstraints(usingToken.doRunJavaConstraints(token2strings));
validityManager.setRunOCLConstraints(usingToken.doRunOCLConstraints(token2strings));
validityManager.setRunUMLConstraints(usingToken.doRunUMLConstraints(token2strings));
validityManager.setInput(resourceSet);
return validityManager;
}
/**
* Loads the entered model and ocl files.
*
* @param modelFilePath
* the model to validate file path.
* @param oclPaths
* the ocl files paths.
* @return true if there is not problem while loading, false otherwise.
*/
private boolean processResources(@NonNull String modelFilePath, @NonNull List<String> oclFileNames) {
boolean allOk = true;
Helper helper = new Helper(standaloneApplication.getResourceSet()) {
@Override
protected boolean error(@NonNull String primaryMessage, @Nullable String detailMessage) {
logger.error(primaryMessage + detailMessage);
return false;
}
};
for (String oclFileName : oclFileNames) {
URI oclURI = getFileUri(oclFileName);
if (allOk && oclURI == null) {
logger.error(MessageFormat.format(StandaloneMessages.OCLValidatorApplication_OclUriProblem, oclFileName));
allOk = false;
}
// Load ocl models
// if (done && standaloneApplication.loadModelFile(oclURI) == null) {
// logger.error(MessageFormat.format(StandaloneMessages.OCLValidatorApplication_OclLoadProblem, oclFileName));
// done = false;
// }
// Load as ocl documents
try {
if (allOk) {
Resource oclResource = helper.loadResource(oclURI);
if (oclResource == null) {
logger.error(MessageFormat.format(StandaloneMessages.OCLValidatorApplication_OclLoadProblem, oclFileName));
allOk = false;
}
}
} catch (Throwable e) {
logger.error(MessageFormat.format(StandaloneMessages.OCLValidatorApplication_OclLoadProblem, oclFileName));
allOk = false;
}
}
if (allOk && !helper.loadMetaModels()) {
logger.error(StandaloneMessages.OCLValidatorApplication_MetaModelsLoadProblem);
allOk = false;
}
helper.installPackages();
return allOk;
}
/**
* Runs the validation
*/
private void validate(@NonNull StandaloneValidityManager validityManager) {
// logger.info(StandaloneMessages.OCLValidatorApplication_ValidationStarting);
validityManager.runValidation();
// logger.info(StandaloneMessages.OCLValidatorApplication_ValidationComplete);
}
}