Package org.openiaml.model.tests.model

Source Code of org.openiaml.model.tests.model.FindUnusedMetamodelFeatures

/**
*
*/
package org.openiaml.model.tests.model;

import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.openiaml.model.ModelLoader;
import org.openiaml.model.ModelLoader.ModelLoadException;
import org.openiaml.model.tests.XmlTestCase;

/**
* Investigate all model instances and locate unused metamodel features.
*
* @author jmwright
*
*/
public class FindUnusedMetamodelFeatures extends XmlTestCase {

  /**
   * Get all possible model instances. This is done by:
   *
   * <ol>
   *   <li>Getting all .iaml files in <code>/infer-output</code>
   <li>Recursing over all folders in the <code>org.openiaml.model.tests</code> plugin for .iaml files
   *  <li>Recursing over all folders in the <code>org.openiaml.model.tests.diagram</code> plugin for .iaml files
   * </ol>
   *
   * @return
   */
  public List<File> getModelInstances() {
    List<File> instances = new ArrayList<File>();
   
    // first, /infer-output
    instances.addAll(recurseOver(new File("infer-output/"), "iaml"));
   
    // then, org.openiaml.model.tests
    instances.addAll(recurseOver(new File("src/"), "iaml"));
   
    // then, org.openiaml.model.tests.diagram
    instances.addAll(recurseOver(new File("../org.openiaml.model.tests.diagram/src/"), "iaml"));
   
    // remove any file that is stored in /examples/, to reduce problems
    // also remove any migration models
    // TODO this code will not be necessary when the /examples/ model folder is removed
    {
      List<File> i2 = new ArrayList<File>();
      for (File f : instances) {
        if (!f.getAbsolutePath().contains("\\examples\\")
            && !f.getAbsolutePath().contains("\\eclipse\\migration\\"))
          i2.add(f);
      }
      instances = i2;
    }
   
    return instances;
   
  }
 
  /**
   * We should have at least one model instance.
   */
  public void testModelInstancesNotEmpty() {
    assertFalse(getModelInstances().isEmpty());
  }
 
  public void testFindUnusedFeatures() throws ModelLoadException, IOException {
   
    // first, make a map of all non-abstract classes to all superclasses
    Map<EClass,Set<EClass>> unusedSuperClassFeaturesMap =
      new HashMap<EClass,Set<EClass>>();
    for (EClass cls : ContainmentTestCase.getAllClasses().keySet()) {
      Set<EClass> supers = new HashSet<EClass>();
      supers.addAll(cls.getEAllSuperTypes());
      unusedSuperClassFeaturesMap.put(cls, supers);
    }
   
    int count = 0;
    List<File> instances = getModelInstances();
    /*
    {
      // get only the first 10
      List<File> i2 = new ArrayList<File>();
      for (int i = 0; i < 10; i++) {
        i2.add(instances.get(i));
      }
      instances = i2;     
    }
    */
   
    for (File f : instances) {
      // print out the progress
      count++;
      System.out.println(f.getName() + ": (" + count + "/" + instances.size() + ")");
     
      // load the file
      EObject root = ModelLoader.load(f.getAbsolutePath());     
      assertNotNull("Could not load file '" + f + "'", root);
     
      // now find unused features
      TreeIterator<EObject> it = root.eAllContents();
      while (it.hasNext()) {
        EObject e = it.next();
        EClass cls = e.eClass();
        // check all attributes
        for (EAttribute attr : cls.getEAllAttributes()) {
          // is it non-default?
          if (e.eGet(attr) != attr.getDefaultValue()) {
            // yes; remove it from the unused features (if it exists)
            EClass container = attr.getEContainingClass();
            unusedSuperClassFeaturesMap.get(cls).remove(container);
          }
        }
       
        // check all references
        for (EReference ref : cls.getEAllReferences()) {
          if (ref.isMany()) {
            // is it empty?
            if (!((List<?>) e.eGet(ref)).isEmpty()) {
              // no; remove it from the unused features (if it exists)
              EClass container = ref.getEContainingClass();
              unusedSuperClassFeaturesMap.get(cls).remove(container);
            }
          } else {
            // is it null?
            if (e.eGet(ref) != null) {
              // no; remove it from the unused features (if it exists)
              EClass container = ref.getEContainingClass();
              unusedSuperClassFeaturesMap.get(cls).remove(container);
            }
          }
        }
      }
     
      // finally, print out all unused types, to an HTML file
      printOutUnusedSuperClassFeatures(unusedSuperClassFeaturesMap);
    }
   
  }
 
  /**
   * Print out the given map to an HTML file.
   *
   * @param unusedSuperClassFeaturesMap
   * @throws IOException
   */
  private void printOutUnusedSuperClassFeatures(
      Map<EClass, Set<EClass>> map) throws IOException {

    FileWriter unused = new FileWriter(new File("unused.html"));
    unused.write("<html><style>html { font-family: Arial; }</style><body>\n\n");
    for (EClass cls : map.keySet()) {
      // get a color
      int unusedCount = map.get(cls).size();
      int totalCount = cls.getEAllSuperTypes().size();
      String color = "";
      if (totalCount != 0) {
        double ratio = (double) unusedCount / (double) totalCount;
        if (ratio >= .75) {
          color = "background-color: #ff6666;";
        } else if (ratio >= .5) {
          color = "background-color: #ffff66;";
        } else if (ratio >= .25) {
          color = "background-color: #66ff66;";
        }
      }
     
      unused.write("<a name=\"");
      unused.write(cls.getName());
      unused.write("\"><h1 style=\"");
      unused.write(color);
      unused.write("\">");
      unused.write(cls.getName());
      unused.write("</h1></a>\n");
     
      // unused features
      unused.write("Unused supertypes (");
      unused.write(Integer.toString(unusedCount));
      unused.write("/");
      unused.write(Integer.toString(totalCount));
      unused.write("):\n");
      unused.write("<ul>");
      for (EClass u : map.get(cls)) {
        unused.write("<li><a href=\"#");
        unused.write(u.getName());
        unused.write("\">");
        if (u.isAbstract()) {
          unused.write("<i>");
        }
        unused.write(u.getName());
        if (u.isAbstract()) {
          unused.write("</i>");
        }
        unused.write("</a>");
        if (u.isInterface() && u.getEReferences().isEmpty() && u.getEAttributes().isEmpty()) {
          unused.write(" (empty interface)");
        }
        // do any of this classes supertypes use the reference?
        EClass usedBy = subTypeUses(map, cls, u);
        if (usedBy != null) {
          unused.write(" (used by <a href=\"#");
          unused.write(usedBy.getName());
          unused.write("\">");
          unused.write(usedBy.getName());
          unused.write("</a>)");
        }
        unused.write("</li>");
      }
      unused.write("</ul>");
    }
 
    unused.write("\n\n</body></html>");
    unused.close();
   
  }

  /**
   * Do any subtypes of this class use the given unused class?
   *
   * @return
   */
  private EClass subTypeUses(Map<EClass, Set<EClass>> map, EClass source, EClass unused) {
    for (EClass cls : map.keySet()) {
      if (cls.getEAllSuperTypes().contains(source)) {
        if (!map.get(cls).contains(unused)) {
          // this class uses it
          return cls;
        }
      }
    }
   
    return null;
  }

  /**
   * Recurse over the given directory, and find all files that have the
   * given extension. Throws an exception if the given file is not a directory.
   *
   * @param f
   * @param extension
   * @return
   */
  private Collection<? extends File> recurseOver(File f,
      final String extension) {
    assertTrue("File '" + f + "' did not exist", f.exists());
    assertTrue("File '" + f + "' is not a directory", f.isDirectory());
    assertFalse("Extension '" + extension + "' should not start with a .", extension.startsWith("."));
   
    // for all files in this folder
    File[] results = f.listFiles(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return name.endsWith("." + extension);
      }
    });
   
    List<File> out = new ArrayList<File>();
    for (File f2 : results) {
      assertTrue("Found file '" + f2 + "' does not exist.", f2.exists());
      out.add(f2);
    }
   
    // recurse over subfolders
    File[] sub = f.listFiles(new FileFilter() {
      @Override
      public boolean accept(File pathname) {
        return pathname.isDirectory();
      }
    });
    for (File s : sub) {
      out.addAll(recurseOver(s, extension));
    }
   
    return out;
  }

}
TOP

Related Classes of org.openiaml.model.tests.model.FindUnusedMetamodelFeatures

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.