Package edu.cmu.cs.crystal.util.typehierarchy

Source Code of edu.cmu.cs.crystal.util.typehierarchy.CachedTypeHierarchy$TypeInfo

package edu.cmu.cs.crystal.util.typehierarchy;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;

import edu.cmu.cs.crystal.util.TypeHierarchy;

public class CachedTypeHierarchy implements TypeHierarchy {
  private HashMap<String, TypeNode> types;
  private IJavaProject project;
 
  private class TypeInfo {
    public String typeName;
    public String generics;
    public boolean isArray;
  }
 
  public CachedTypeHierarchy(IJavaProject project) throws JavaModelException {
    this.project = project;
    types = new HashMap<String, TypeNode>();
    loadNewTree("java.lang.Object");
    defaults();
  }
 
  public boolean existsCommonSubtype(String t1, String t2) {
    return existsCommonSubtype(t1, t2, false, false);
  }
 
  public boolean existsCommonSubtype(String t1, String t2, boolean skipCheck1, boolean skipCheck2) {
    if (t1.equals("java.lang.Object") || t2.equals("java.lang.Object"))
      return true;

    if (t1.equals("void"))
      return t2.equals("void");
    if (t2.equals("void"))
      return false;

    TypeInfo type1 = getTypeAndGenerics(t1);
    TypeInfo type2 = getTypeAndGenerics(t2);
   
    if (type1.isArray != type2.isArray)
      return false;
   
    TypeNode node1 = getOrCreateType(type1.typeName);
    TypeNode node2 = getOrCreateType(type2.typeName);

    if (node1 == null || node2 == null)
      return false;
   
    //need to deal with primitives early? also equality of strings passed in
    if (node1.isPrimitive())
      return node2.isPrimitive();
    if (node2.isPrimitive())
      return false;
   
    if (!skipCheck1 && isSubtypeCompatible(type1.typeName, type2.typeName)) {
      return existsCommonSubtypeGenerics(type1.generics, type2.generics);
    }
   
    if (!skipCheck2 && isSubtypeCompatible(type2.typeName, type1.typeName)) {
      return existsCommonSubtypeGenerics(type1.generics, type2.generics);
    }
 
    if (!node1.isCompleteDown())
      loadNewTree(type1.typeName);
    if (!node2.isCompleteDown())
      loadNewTree(type2.typeName);

    HashSet<String> t1Subs = new HashSet<String>();
    HashSet<String> t2Subs = new HashSet<String>();
   
    node1.collectAllSubs(t1Subs);
    node2.collectAllSubs(t2Subs);
   
    for (String sub : t1Subs) {
      if (t2Subs.contains(sub))
        return existsCommonSubtypeGenerics(type1.generics, type2.generics);
    }
    return false;
  }
 
  TypeInfo getTypeAndGenerics(String fullType) {
    int genStart = fullType.indexOf('<');
    TypeInfo info = new TypeInfo();
   
    if (genStart != -1) {
      info.typeName = fullType.substring(0, genStart);
      info.generics = fullType.substring(genStart + 1, fullType.lastIndexOf('>'));
    }
    else {
      info.typeName = fullType;
      info.generics = "";
    }
   
    info.isArray = info.typeName.endsWith("[]");
    if (info.isArray)
      info.typeName = info.typeName.substring(0, info.typeName.length() - 2);
   
    return info;
  }
 

  /**
   * Get the node out of the type map. If it doesn't exist, then
   * create it!
   */
  private TypeNode getOrCreateType(String qualifiedName) {
    TypeNode node = types.get(qualifiedName);
    if (node == null) {
      node = new TypeNode(qualifiedName);
      types.put(qualifiedName, node);
    }
    return node;
  }

  /**
   * @param gen1 An empty or comma separated list of generics
   * @param gen2 An empty or comma separated list of generics
   * @return
   */
  private boolean existsCommonSubtypeGenerics(String list1, String list2) {
    if (list1.equals("") || list2.equals(""))
      return true;
   
    List<List<Integer>> bracketPairs1 = bracketIndices(list1);
    List<List<Integer>> bracketPairs2 = bracketIndices(list2);
   
    List<String> gen1 = commaSplit(list1, bracketPairs1);
    List<String> gen2 = commaSplit(list2, bracketPairs2);
       
    if (gen1.size() != gen2.size())
      return false;
   
    for (int ndx = 0; ndx < gen1.size(); ndx++) {
      if (!existsCommonSubtype(gen1.get(ndx).trim(), gen2.get(ndx).trim()))
        return false;
    }
    return true;
  }

  public boolean isSubtypeCompatible(String subTypeFullName, String superTypeFullName) {
    if (superTypeFullName.equals("java.lang.Object"))
      return true;
   
    if (subTypeFullName.equals("void"))
      return superTypeFullName.equals("void");
    if (superTypeFullName.equals("void"))
      return false;
   
    TypeInfo subType = getTypeAndGenerics(subTypeFullName);
    TypeInfo supType = getTypeAndGenerics(superTypeFullName);

    if (subType.isArray != supType.isArray)
      return false;
   
    TypeNode subNode = getOrCreateType(subType.typeName);
    TypeNode superNode = getOrCreateType(supType.typeName);
   
    if (subNode == null || superNode == null)
      return false;
   
    if (subNode.isPrimitive())
      return superNode.isPrimitive();
    if (superNode.isPrimitive())
      return false;

   
    if (!superNode.isCompleteDown())
      loadNewTree(supType.typeName);
   
    //now we have all the info
    return subNode.isSupertype(superNode) && isSubtypeCompatibleGenerics(subType.generics, supType.generics);   
  }
 
  /**
   * @param gen1 An empty or comma separated list of generics
   * @param gen2 An empty or comma separated list of generics
   * @return
   */
  private boolean isSubtypeCompatibleGenerics(String listSub, String listSuper) {
    if (listSuper.equals(""))
      return true;
    if (listSub.equals(""))
      return false;

    List<List<Integer>> bracketPairsSub = bracketIndices(listSub);
    List<List<Integer>> bracketPairsSuper = bracketIndices(listSuper);
   
    List<String> genSub = commaSplit(listSub, bracketPairsSub);
    List<String> genSuper = commaSplit(listSuper, bracketPairsSuper);
       
    if (genSub.size() != genSuper.size())
      return false;
   
    for (int ndx = 0; ndx < genSub.size(); ndx++) {
      if (!isSubtypeCompatible(genSub.get(ndx).trim(), genSuper.get(ndx).trim()))
        return false;
    }
    return true
  }

  private List<List<Integer>> bracketIndices(String s) {
    List<List<Integer>> pairsOfIndices = new ArrayList<List<Integer>>();
    Stack<Integer> stack = new Stack<Integer>(); // stack of open brackets
    int current = 0;
    while (current < s.length()) {
      int openIndex = s.indexOf('<', current);
      int closeIndex = s.indexOf('>', current);
      if (openIndex > 0 && closeIndex > 0) { // both found
        if (openIndex < closeIndex) { // open comes first
          stack.push(openIndex);
          current = openIndex+1;
        } else { // close comes first
          List<Integer> pair = new LinkedList<Integer>();
          pair.add(stack.pop()); // open
          pair.add(closeIndex); // close
          pairsOfIndices.add(pair);
          current = closeIndex+1;
        }
      } else if (closeIndex > 0) { // only close found
        List<Integer> pair = new LinkedList<Integer>();
        pair.add(stack.pop()); // open
        pair.add(closeIndex); // close
        pairsOfIndices.add(pair);
        current = closeIndex+1;
      } else if (openIndex > 0) { // only open found --> something went wrong
        throw new RuntimeException("Error while parsing generics: "+s);
      } else { // no (more) brackets found
        assert(stack.isEmpty());
        break;
      }
    }
    return pairsOfIndices;
  }
 
  /**
   * Split at commas, but exclude commas in the given ranges.
   * @return
   */
  private List<String> commaSplit(String s, List<List<Integer>> excludedRanges) {
    List<Integer> commas = new ArrayList<Integer>();
    int current = 0;
    while (current < s.length()) {
       int index = s.indexOf(',', current);
       if (index < 0) break;
       boolean excluded = false;
       for (List<Integer> pair : excludedRanges) {
         if (index >= pair.get(0) & index <= pair.get(1)) {
           excluded = true;
           break;
         }
      }
       if (!excluded) commas.add(index);
       current = index+1;
    }
   
    List<String> splitted = new ArrayList<String>();
    current = 0;
    for (Integer commaIndex : commas) {
      splitted.add(s.substring(current, commaIndex));
      current = commaIndex+1;
    }
    splitted.add(s.substring(current, s.length()));
   
    return splitted;
  }

  /**
   *
   * @param qName
   * @param doClasses
   * @return the new typeNode for this type, fully completed
   */
  private void loadNewTree(String qName) {
    try {
      IType baseType = project.findType(qName);
     
      if (baseType != null) {
        ITypeHierarchy hierarchy = baseType.newTypeHierarchy(project, null);   
        addInHierarchy(baseType, hierarchy);     
       
        //Yeah...that just wasted a bunch of resources. Clean up now...
        Runtime r = Runtime.getRuntime();
        r.gc();
      }
    } catch (JavaModelException e) {
      //can't really do anything...
      e.printStackTrace();
    }
  }
 
  private void addInHierarchy(IType type, ITypeHierarchy hierarchy) throws JavaModelException {
    String qName = type.getFullyQualifiedName('.');
    TypeNode node = getOrCreateType(qName);
   
    if (node.isCompleteDown())
      return;
   
    //Recurse on children
    for (IType sub : hierarchy.getSubtypes(type)) {
      String subName = sub.getFullyQualifiedName('.');
      TypeNode subNode = getOrCreateType(subName);
     
      node.addSubtype(subNode);
      subNode.addSupertype(node);
      addInHierarchy(sub, hierarchy);
    }
    //we now have everything below this node in the hierarchy.
    node.completedDown();
  }


  private void defaults() {
    TypeNode intNode = new TypeNode("int", true);
    TypeNode shortNode = new TypeNode("short", true);
    TypeNode longNode = new TypeNode("long", true);
    TypeNode charNode = new TypeNode("char", true);
    TypeNode boolNode = new TypeNode("boolean", true);
    TypeNode doubleNode = new TypeNode("double", true);
    TypeNode floatNode = new TypeNode("float", true);
    TypeNode voidNode = new TypeNode("void", true);
    TypeNode byteNode = new TypeNode("byte", true);
   
    types.put("void", voidNode);
    types.put("char", charNode);
    types.put("byte", byteNode);
    types.put("short", shortNode);
    types.put("int", intNode);
    types.put("long", longNode);
    types.put("boolean", boolNode);
    types.put("float", floatNode);
    types.put("double", doubleNode);
   
    shortNode.addSubtype(byteNode);
    byteNode.addSupertype(shortNode);
   
    intNode.addSubtype(shortNode);
    intNode.addSubtype(byteNode);
    byteNode.addSupertype(intNode);
    shortNode.addSupertype(intNode);
   
    longNode.addSubtype(shortNode);
    longNode.addSubtype(intNode);
    longNode.addSubtype(byteNode);
    byteNode.addSupertype(longNode);
    shortNode.addSupertype(longNode);
    intNode.addSupertype(longNode);
   
    charNode.addSupertype(longNode);
    longNode.addSubtype(charNode);
    charNode.addSupertype(intNode);
    intNode.addSubtype(charNode);
    shortNode.addSupertype(charNode);
    charNode.addSubtype(shortNode);
    byteNode.addSupertype(charNode);
    charNode.addSubtype(byteNode);
   
    doubleNode.addSubtype(floatNode);
    floatNode.addSupertype(doubleNode);

    floatNode.addSubtype(longNode);
    longNode.addSupertype(floatNode);
   
    byteNode.completedDown();
    shortNode.completedDown();
    intNode.completedDown();
    longNode.completedDown();
    doubleNode.completedDown();
    floatNode.completedDown();
    charNode.completedDown();
    boolNode.completedDown();
    voidNode.completedDown();
  }
}
TOP

Related Classes of edu.cmu.cs.crystal.util.typehierarchy.CachedTypeHierarchy$TypeInfo

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.