Package org.openiaml.verification.crocopat

Source Code of org.openiaml.verification.crocopat.VerificationEngine$TwoWayMap

/**
*
*/
package org.openiaml.verification.crocopat;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.sosy_lab.crocopat.cli.ExecuteCrocopat;
import org.sosy_lab.crocopat.cli.ExecuteCrocopat.CrocopatException;

/**
* An abstract engine to verify with CrocoPat.
* This way we can specify different types of verification inputs
* with different validation rules, etc; similar to Drools.
*
* @author jmwright
*
*/
public class VerificationEngine {
 
  private List<VerificationViolation> violations;
 
  /**
   * Get all of the violations found from the last execution of
   * {@link #verify(EObject)}.
   *
   * @return
   */
  public List<VerificationViolation> getViolations() {
    return violations;
  }
 
  /**
   * Verify a given model. Violations can then be obtained by
   * {@link #getViolations()}.
   *
   * @param model
   * @return OK if verification was successful; Warning if a violation was found; Error otherwise
   * @throws VerificationException if verification could not be executed
   */
  public IStatus verify(EObject model, IProgressMonitor monitor) throws VerificationException {
   
    try {
      monitor.beginTask("Verifying with CrocoPat", 10);
     
      // we want to write the model and rules to files
      File rsfFile = File.createTempFile("temp", ".rsf");
      FileWriter rsf = new FileWriter(rsfFile);
      File rmlFile = File.createTempFile("temp", ".rml");
      FileWriter rml = new FileWriter(rmlFile);
     
      if (!deleteTemporaryFiles()) {
        // print out the locations of the files
        System.out.println(rsfFile);
        System.out.println(rmlFile);
      }
     
      // construct the instance map
      monitor.subTask("Constructing instance map");
      TwoWayMap<String, EObject> instanceMap =
        constructIDMap(model);
      monitor.worked(1);
     
      // export types to rsf
      monitor.subTask("Exporting meta-model to RSF");
      exportTypes(model.eClass().getEPackage(), rsf);
      // and ecore metamodel as well
      exportTypes(EcorePackage.eINSTANCE, rsf);
     
      // export references
      exportReferences(model.eClass().getEPackage(), rsf);
      // and ecore metamodel as well
      exportReferences(EcorePackage.eINSTANCE, rsf);
     
      // export the rest of the universe to rsf
      exportUniverses(rsf);
      monitor.worked(1);

      // export model instance to rsf
      monitor.subTask("Exporting model instance to RSF");
      exportInstance(model, instanceMap, rsf);
      Iterator<EObject> it = model.eAllContents();
      while (it.hasNext()) {
        exportInstance(it.next(), instanceMap, rsf);
      }
      monitor.worked(1);
           
      // export inheritance to rml
      monitor.subTask("Exporting RML");
      exportInheritance(model.eClass().getEPackage(), rml);
      // and ecore metamodel as well
      exportInheritance(EcorePackage.eINSTANCE, rml);
     
      // export all the rules to rml
      exportRules(rml);
      monitor.worked(1);
     
      // save the files
      rsf.close();
      rml.close();
     
      // get the executor
      ExecuteCrocopat exec = new ExecuteCrocopat();
      InputStream rsfin = new FileInputStream(rsfFile);
      InputStream rmlin = new FileInputStream(rmlFile);
 
      // execute
      monitor.subTask("Calling CrocoPat");
      List<String> results;
      try {
        results = exec.execute(rmlin, rsfin);
      } catch (CrocopatException e) {
        throw new VerificationException("Could not execute CrocoPat: " + e.getMessage(), e);
      }
      monitor.worked(5);
     
      // parse for violations
      monitor.subTask("Parsing output");
      violations = parseViolations(model, results, instanceMap);
      monitor.worked(1);
     
      // delete temporary files
      if (deleteTemporaryFiles()) {
        rsfFile.delete();
        rmlFile.delete();
      }
     
      // return
      monitor.done();
      if (violations.isEmpty()) {
        return Status.OK_STATUS;
      } else {
        return new Status(IStatus.WARNING,
            VerificationCrocopatPlugin.PLUGIN_ID,
            "CrocoPat verification failed: " + violations.size() + " violations");
      }
     
    } catch (IOException e) {
      return errorStatus(e);
    }
  }
 
  /**
   * Should the generated .rml and .rsf files be deleted?
   *
   * @return
   */
  public boolean deleteTemporaryFiles() {
    return false;
  }

  public IStatus errorStatus(String message, Throwable e) {
    return new Status(IStatus.ERROR, VerificationCrocopatPlugin.PLUGIN_ID, message, e);
  }
 
  public IStatus errorStatus(Throwable e) {
    return new Status(IStatus.ERROR, VerificationCrocopatPlugin.PLUGIN_ID, e.getMessage(), e);
  }

  /**
   * From a list of output from Crocopat, generate the list of violations.
   *
   * @param model
   * @param results
   * @param instanceMap
   * @return
   * @throws VerificationException if parsing failed
   */
  protected List<VerificationViolation> parseViolations(EObject model, List<String> results, TwoWayMap<String, EObject> instanceMap) throws VerificationException {
    List<VerificationViolation> violations = new ArrayList<VerificationViolation>();
    for (String s : results) {
      // expected format: [message] tab [id1] tab [...] tab [idn] (tab)
     
      String[] bits = s.trim().split("\t");
      // needs to be at least two bits to add a violation
      if (bits.length > 1) {
        // a new violation
        List<EObject> objects = new ArrayList<EObject>();
       
        // where is this object in the model?
        for (int i = 1; i < bits.length; i++) {
          String key = bits[i].trim();
          // remove quotes
          if (key.startsWith("\""))
            key = key.substring(1);
          if (key.endsWith("\""))
            key = key.substring(0, key.length() - 1);
         
          if (instanceMap.containsKey(key)) {
            objects.add(instanceMap.get(key));
          }
        }
       
        // construct the new violation
        VerificationViolation v = new VerificationViolation(objects, bits[0]);
        violations.add(v);
      }
    }
   
    // return
    return violations;
  }
 
  /**
   * Represents a two-way hash map, so that operations going
   * either way are fast.
   *
   * @author jmwright
   *
   * @param <A>
   * @param <B>
   */
  public class TwoWayMap<A, B> {
   
    private Map<A, B> aToB = new HashMap<A, B>();
    private Map<B, A> bToA = new HashMap<B, A>();
   
    public void put(A key, B value) {
      aToB.put(key, value);
      bToA.put(value, key);
    }
   
    public A getKey(B value) {
      return bToA.get(value);
    }
   
    public B get(A key) {
      return aToB.get(key);
    }

    public int size() {
      return aToB.size();
    }

    public boolean containsKey(A key) {
      return aToB.containsKey(key);
    }

    public boolean containsValue(B value) {
      return bToA.containsKey(value);
    }
   
  }
 
  /**
   * Take the given model and turn it into a map of unique EObjects.
   *
   * @param model
   * @return
   */
  private TwoWayMap<String, EObject> constructIDMap(EObject model) {
   
    TwoWayMap<String, EObject> map = new TwoWayMap<String, EObject>();
   
    map.put(getMapID(map, model), model);
    Iterator<EObject> it = model.eAllContents();
    while (it.hasNext()) {
      EObject obj = it.next();
      map.put(getMapID(map, obj), obj);
    }
   
    return map;
   
  }
 
  private String getMapID(TwoWayMap<String, EObject> map, EObject obj) {
    return "_unique_" + map.size();
  }
 
  /**
   * Export all of the rules necessary.
   *
   * @param rml
   * @throws IOException
   */
  protected void exportRules(FileWriter rml) throws IOException {
    for (String file : getRuleFiles()) {
      // open rule file from bundle
      URL url;
      try {
        url = VerificationCrocopatPlugin.getInstance().getResolvedFile(file);
      } catch (NullPointerException e) {
        // don't throw it if it was from getInstance()
        if (VerificationCrocopatPlugin.getInstance() == null)
          throw e;
       
        throw new RuntimeException("Unexpectedly got a NullPointerException while finding bundle file '" + file + "': perhaps the file does not exist?: " + e.getMessage(), e);
      }
     
      // open input stream
      InputStream is = url.openStream();
      int c;
      while ((c = is.read()) != -1) {
        rml.write(c);
      }
      is.close();
     
    }
  }
 
  /**
   * Get all of the rule files in the current bundle.
   *
   * @return
   */
  public List<String> getRuleFiles() {
    List<String> result = new ArrayList<String>();
   
    result.add("rules/infinite_redirection.rml");
   
    return result;
  }

 
  /**
   * Export all of the universes necessary.
   *
   * @param rml
   * @throws IOException
   */
  protected void exportUniverses(FileWriter rml) throws IOException {
    for (String file : getUniverseFiles()) {
      // open rule file from bundle
      URL url;
      try {
        url = VerificationCrocopatPlugin.getInstance().getResolvedFile(file);
      } catch (NullPointerException e) {
        // don't throw it if it was from getInstance()
        if (VerificationCrocopatPlugin.getInstance() == null)
          throw e;
       
        throw new RuntimeException("Unexpectedly got a NullPointerException while finding bundle file '" + file + "': perhaps the file does not exist?: " + e.getMessage(), e);
      }
     
      // open input stream
      InputStream is = url.openStream();
      int c;
      while ((c = is.read()) != -1) {
        rml.write(c);
      }
      is.close();
     
    }
  }
 
  /**
   * Get all of the rule files in the current bundle.
   *
   * @return
   */
  public List<String> getUniverseFiles() {
    List<String> result = new ArrayList<String>();
   
    result.add("rules/infinite_redirection.rsf");
   
    return result;
  }

  /**
   * Export the model instance. Is not recursive.
   *
   * @param instanceMap
   * @throws IOException
   *
   */
  private void exportInstance(EObject model, TwoWayMap<String, EObject> instanceMap, Writer rsf) throws IOException {
    // define that it exists
    rsf.write("\n");
    rsf.write("instance_");
    rsf.write(model.eClass().getName());
    rsf.write(" \"");
    rsf.write(getID(model, instanceMap));
    rsf.write("\"\n");;
   
    // iterate over all references
    for (EReference ref : model.eClass().getEAllReferences()) {
      Object result = model.eGet(ref);
      if (!ref.isMany()) {
        result = Collections.singletonList(result);
      }
      // for every result
      List<?> resultList = (List<?>) result;
      for (Object obj : resultList) {
        if (obj != null) {
          rsf.append(getReferenceName(ref));
          rsf.write(" \"");
          rsf.write(getID(model, instanceMap));
          rsf.write("\" \"");
          rsf.write(getID(obj, instanceMap));
          rsf.write("\"\n");
        }
      }
    }
   
    // iterate over all attributes
    for (EAttribute attr : model.eClass().getEAllAttributes()) {
      Object result = model.eGet(attr);
      if (!attr.isMany()) {
        result = Collections.singletonList(result);
      }

      // for every result
      List<?> resultList = (List<?>) result;
      for (Object obj : resultList) {
        if (obj != null) {
          rsf.append(getAttributeName(attr));
          rsf.write(" \"");
          rsf.write(getID(model, instanceMap));
          rsf.write("\" \"");
          rsf.write(getID(obj, instanceMap));
          rsf.write("\"\n");
        }
      }
    }
  }

  /**
   * @param ref
   * @return
   */
  private String getReferenceName(EReference ref) {
    return ref.getName();
  }

  /**
   * @param ref
   * @return
   */
  private String getAttributeName(EAttribute ref) {
    return ref.getName();
  }

  /**
   * @param model
   * @return
   */
  private String getID(Object model, TwoWayMap<String,EObject> instanceMap) {
    if (model instanceof EObject) {
      // is it in the instance map?
      if (instanceMap.containsValue((EObject) model)) {
        return instanceMap.getKey((EObject) model);
      }
    }
   
    if (model instanceof String) {
      return model.toString();
    } else if (model instanceof Number) {
      return model.toString();
    } else if (model instanceof Boolean) {
      return model.toString();
    } else {
      return new StringBuffer().append("_o").append(model.hashCode()).toString();
    }
  }

  /**
   * Construct a map of all classes to subclasses.
   * @param map
   * @param pkg
   */
  private void constructInheritanceMap(Map<EClass,Set<EClass>> map, EPackage ePackage) {
    for (EClassifier clsfr : ePackage.getEClassifiers()) {
      if (clsfr instanceof EClass) {
        EClass cls = (EClass) clsfr;

        // add this one directly
        if (!map.containsKey(cls)) {
          map.put(cls, new HashSet<EClass>());
        }

        // does this class have any direct supertypes?
        for (EClass sup : cls.getESuperTypes()) {
          if (!map.containsKey(sup)) {
            map.put(sup, new HashSet<EClass>());
          }
          // add this as a subtype
          map.get(sup).add(cls);
        }
      }
    }
   
    for (EPackage pkg : ePackage.getESubpackages()) {
      constructInheritanceMap(map, pkg);
    }
  }
 
  /**
   * Export the given class. If the given classes supertypes
   * have not been exported yet, export them first, to prevent
   * warnings in Crocopat.
   *
   * @param cls
   * @param map
   * @throws IOException
   */
  private void exportInheritance(Set<EClass> exported, EClass cls, Map<EClass,Set<EClass>> map, Writer rml) throws IOException {
    // check all subtypes first, to make sure they are exported
    for (EClass sub : map.get(cls)) {
      if (!exported.contains(sub)) {
        // export this subtype first
        exportInheritance(exported, sub, map, rml);
      }
    }
   
    // we have exported this class
    exported.add(cls);
   
    rml.write(cls.getName());
    rml.write("(x) := x != \"null\" & (");
 
    // add current instances
    rml.write("instance_");
    rml.write(cls.getName());
    rml.write("(x)");
   
    // print out a list of its direct subtypes
    for (EClass sub : map.get(cls)) {
      rml.write(" | ");
      rml.write(sub.getName());
      rml.write("(x)");
    }
   
    rml.write(");\n");
  }
 
  /**
   * @param ePackage
   * @param rml
   * @throws IOException
   */
  private void exportInheritance(EPackage ePackage, Writer rml) throws IOException {
    // first, construct the map
    Map<EClass,Set<EClass>> map = new HashMap<EClass,Set<EClass>>();
    constructInheritanceMap(map, ePackage);
   
    // now for every supertype
    Set<EClass> exported = new HashSet<EClass>();
    for (EClass cls : map.keySet()) {
      exportInheritance(exported, cls, map, rml);
    }
  }

  /**
   * @param ePackage
   * @param rsf
   * @throws IOException
   */
  private void exportTypes(EPackage ePackage, Writer rsf) throws IOException {
    for (EClassifier clsfr : ePackage.getEClassifiers()) {
      if (clsfr instanceof EClass) {
        EClass cls = (EClass) clsfr;
       
        rsf.write("instance_");
        rsf.write(cls.getName());
        rsf.write(" null\n");
      }
    }
   
    for (EPackage pkg : ePackage.getESubpackages()) {
      exportTypes(pkg, rsf);
    }
  }
 
  /**
   * Export null references for all references and attributes. This prevents
   * Crocopat from throwing 'Warning: Undefined variable ...'
   * for references that do not exist in a model.
   *
   * @throws IOException
   */
  private void exportReferences(EPackage ePackage, Writer rsf) throws IOException {
    exportReferences(new HashSet<String>(), ePackage, rsf);
  }
 
  /**
   * @param references references and attributes already written
   * @throws IOException
   */
  private void exportReferences(Set<String> references, EPackage ePackage, Writer rsf) throws IOException {
    for (EClassifier clsfr : ePackage.getEClassifiers()) {
      if (clsfr instanceof EClass) {
        EClass cls = (EClass) clsfr;
       
        for (EReference ref : cls.getEAllReferences()) {
          if (!references.contains(ref.getName())) {
            references.add(ref.getName());
            rsf.write(getReferenceName(ref));
            rsf.write(" \"null\" \"null\"\n");
          }
        }
       
        for (EAttribute attr : cls.getEAllAttributes()) {
          if (!references.contains(attr.getName())) {
            references.add(attr.getName());
            rsf.write(getAttributeName(attr));
            rsf.write(" \"null\" \"null\"\n");
          }
        }
       
      }
    }
   
    // recurse
    for (EPackage pkg : ePackage.getESubpackages()) {
      exportReferences(references, pkg, rsf);
    }
  }
 
}
TOP

Related Classes of org.openiaml.verification.crocopat.VerificationEngine$TwoWayMap

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.