All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.codeAnalysis.CompiledClassUtils Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.codeAnalysis;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.gemstone.gemfire.codeAnalysis.decode.CompiledClass;
import com.gemstone.gemfire.codeAnalysis.decode.CompiledField;
import com.gemstone.gemfire.codeAnalysis.decode.CompiledMethod;



public class CompiledClassUtils {
  /**
   * Parse the given class files and return a map of name->Dclass.  Any
   * IO exceptions are consumed by this method and written to stderr.
   * @param classFiles
   * @return the parsed classes
   */
  public static Map parseClassFiles(List classFiles) {
    Map result = new HashMap();
    
    for (File file: classFiles) {
      try {
        CompiledClass parsed = CompiledClass.getInstance(file);
        if (!parsed.isInterface()) {
          result.put(parsed.fullyQualifiedName(), parsed);
        }
      } catch (IOException e) {
        System.err.println("Exception while parsing " + file.getName() + ": " + e.getMessage());
      }
    }
    return result;
  }
  
  /**
   * Parse the files in the given jar file and return a map of name->CompiledClass.
   * Any IO exceptions are consumed by this method and written to stderr.
   * @param jar the jar file holding classes
   */
  public static Map parseClassFiles(File jar) {
    Map result = new HashMap();
    try {
      JarFile jarfile = new JarFile(jar);
      for (Enumeration entries=jarfile.entries(); entries.hasMoreElements(); ) {
        JarEntry entry = entries.nextElement();
        if (entry.getName().endsWith(".class")) {
          try {
            CompiledClass parsed = CompiledClass.getInstance(jarfile.getInputStream(entry));
            if (!parsed.isInterface()) {
              result.put(parsed.fullyQualifiedName(), parsed);
            }
          } catch (IOException e) {
            System.err.println("Exception while parsing " + entry.getName() + ": " + e.getMessage());
          }
        }
      }
    } catch (IOException e) {
      System.err.println("Error opening jar file:");
      e.printStackTrace(System.err);
    }
    return result;
  }
  
  /**
   * returns a collection of all of the .class files in the given list
   * of files and directories.
   * 
   * @param filenames a list of the files and directories to examine
   * @param recursive whether to recurse into subdirectories
   * @return a sorted list of the .class files found
   */
  public static List findClassFiles(String parentPath, String[] filenames, boolean recursive) {
    // Grab classes and Expand directory names found in list
    List classFiles = new ArrayList();
    for (int i = 0; i <  filenames.length; i ++) {
      File f = new File(parentPath+filenames[i]);
      String n = f.getAbsolutePath();
      if (!f.exists()) {
        System.err.println("File " + n + " does not exist - skipping");
        continue;
      }
      if (f.isFile() && f.getName().endsWith(".class")) {
        classFiles.add(f);
        continue;
      }
      if (f.isDirectory() && recursive) {
        classFiles.addAll(findClassFiles(f.getAbsolutePath()+"/", f.list(), true));
      }
    }
    Collections.sort(classFiles);
    return classFiles;
  }

  public static List loadClassesAndMethods(File file) throws IOException {
    List result = new LinkedList();
    FileReader fr = new FileReader(file);
    LineNumberReader in = new LineNumberReader(fr);
    ClassAndMethodDetails cam;
    while ((cam = ClassAndMethodDetails.create(in)) != null) {
      result.add(cam);
    }
    fr.close();
    return result;
  }

  public static String diffSortedClassesAndMethods(
      List goldRecord, List toDatas) throws IOException {
    
    StringBuilder newClassesSb = new StringBuilder(10000);
    StringBuilder changedClassesSb = new StringBuilder(10000);
    newClassesSb.append("New or moved classes----------------------------------------\n");
    int newBase = newClassesSb.length();
    changedClassesSb.append("Modified classes--------------------------------------------\n");
    int changedBase = changedClassesSb.length();
    
    Iterator it = toDatas.iterator();
    ClassAndMethods newclass = null;
    
    for (ClassAndMethodDetails gold: goldRecord) {
      if (newclass == null) {
        if (!it.hasNext()) {
          changedClassesSb.append(gold).append(": deleted or moved\n");
          continue;
        }
        newclass = it.next();
      }
      int comparison = -1;
      while (newclass != null
          && (comparison=gold.className.compareTo(newclass.dclass.fullyQualifiedName())) > 0) {
        newClassesSb.append(newclass).append("\n");
        if (it.hasNext()) {
          newclass = it.next();
        } else {
          newclass = null;
        }
      }
      if (comparison == 0) {
        ClassAndMethods nc = newclass;
        newclass = null;
        if (gold.methodCode.size() != nc.numMethods()) {
          changedClassesSb.append(nc).append(": method count\n");
          continue;
        }
        boolean comma = false;
        for (Map.Entry entry: nc.methods.entrySet()) {
          CompiledMethod method = entry.getValue();
          String name = method.name();
          byte[] goldCode = gold.methodCode.get(name);
          if (goldCode == null) {
            if (comma) {
              changedClassesSb.append(", and ");
            } else {
              changedClassesSb.append(nc).append(":  ");
              comma = true;
            }
            changedClassesSb.append(name).append(" was added");
            continue; // only report one diff per class
          }
          String diff;
          if ((diff = codeDiff(goldCode, method.getCode().code)) != null) {
            if (comma) {
              changedClassesSb.append(", and ");
            } else {
              changedClassesSb.append(nc).append(":  ");
              comma = true;
            }
            changedClassesSb.append(name).append(diff);
            continue;
          }
        }
        for (Map.Entry entry: gold.methodCode.entrySet()) {
          if (!nc.methods.containsKey(entry.getKey())) {
            if (comma) {
              changedClassesSb.append(", and ");
            } else {
              changedClassesSb.append(nc).append(":  ");
            }
            changedClassesSb.append(entry.getKey()).append(" is missing");
          }
        }
        if (comma) {
          changedClassesSb.append("\n");
        }
      }
    }
    while (it.hasNext()) {
      newclass = it.next();
      newClassesSb.append(newclass).append(": new class\n");
    }
    String result = "";
    if (newClassesSb.length() > newBase) {
      if (changedClassesSb.length() > changedBase) {
        newClassesSb.append("\n");
        newClassesSb.append(changedClassesSb);
      }
      result = newClassesSb.toString();
    } else if (changedClassesSb.length() > changedBase) {
      result = changedClassesSb.toString();
    }
    return result;
  }

  
  
  public static void storeClassesAndMethods(List cams,
      File file) throws IOException {
    FileWriter fw = new FileWriter(file);
    BufferedWriter out = new BufferedWriter(fw);
    for (ClassAndMethods entry: cams) {
      out.append(ClassAndMethodDetails.convertForStoring(entry));
      out.newLine();
    }
    out.flush();
    out.close();
  }

  static String codeDiff(byte[] method1, byte[] method2) {
    if (method1.length != method2.length) {
      return " (len="+method2.length+",expected="+method1.length+")";
    }
    for (int i=0; i loadClassesAndVariables(File file) throws IOException {
    List result = new LinkedList();
    FileReader fr = new FileReader(file);
    BufferedReader in = new BufferedReader(fr);
    String line;
    while ((line = in.readLine()) != null) {
      line = line.trim();
      if (line.startsWith("#") || line.startsWith("//")) {
        // comment line
      }
      else {
        result.add(new ClassAndVariableDetails(line));
      }
    }
    fr.close();
    return result;
  }

  public static String diffSortedClassesAndVariables(
      List goldRecord, List cavs) throws IOException {
    
    StringBuilder newClassesSb = new StringBuilder(10000);
    StringBuilder changedClassesSb = new StringBuilder(10000);
    newClassesSb.append("New or moved classes----------------------------------------\n");
    int newBase = newClassesSb.length();
    changedClassesSb.append("Modified classes--------------------------------------------\n");
    int changedBase = changedClassesSb.length();
    
    Iterator it = cavs.iterator();
    ClassAndVariables newclass = null;
    
    List added = new ArrayList();
    List removed = new ArrayList();
    List changed = new ArrayList();
    
    for (ClassAndVariableDetails gold: goldRecord) {
      added.clear(); removed.clear(); changed.clear();
      if (newclass == null) {
        if (!it.hasNext()) {
          changedClassesSb.append(gold).append(": deleted or moved\n");
          continue;
        }
        newclass = it.next();
      }
      int comparison = -1;
      while (newclass != null
          && (comparison=gold.className.compareTo(newclass.dclass.fullyQualifiedName())) > 0) {
        newClassesSb.append(ClassAndVariableDetails.convertForStoring(newclass)).append("\n");
        newclass = null;
        if (it.hasNext()) {
          newclass = it.next();
        }
      }
      if (comparison == 0) {
        ClassAndVariables nc = newclass;
        newclass = null;
//        if (gold.variables.size() != nc.variables.size()) {
//          changedClassesSb.append(nc).append(": field count\n");
//          continue;
//        }
        for (Map.Entry entry: nc.variables.entrySet()) {
          CompiledField field = entry.getValue();
          String name = entry.getKey();
          String type = gold.variables.get(name);
          if (type == null) {
//            if (comma) {
//              changedClassesSb.append(", and ");
//            } else {
//              changedClassesSb.append(nc).append(":  ");
//              comma = true;
//            }
            added.add(name);
            continue; // only report one diff per class
          }
          String newType = field.descriptor();
          if (!newType.equals(type)) {
            changed.add(name + " type changed to " + newType);
            continue;
          }
        }
        for (Map.Entry entry: gold.variables.entrySet()) {
          if (!nc.variables.containsKey(entry.getKey())) {
            removed.add(entry.getKey());
          }
        }
        if (!(added.isEmpty() && removed.isEmpty() && changed.isEmpty())) {
          changedClassesSb.append(nc).append('\n');
        }
        if (!added.isEmpty()) {
          changedClassesSb.append("\t\t added fields: ").append(added).append('\n');
        }
        if (!changed.isEmpty()) {
          changedClassesSb.append("\t\t changed fields: ").append(changed).append('\n');
        }
        if (gold.hasSerialVersionUID) {
          if (nc.hasSerialVersionUID) {
            if (!Long.valueOf(gold.serialVersionUID).equals(nc.serialVersionUID)) {
              changedClassesSb.append("\t\t " + nc.dclass.fullyQualifiedName() + " serialVersionUID was changed from " + gold.serialVersionUID + " to " + nc.serialVersionUID + " this may break client/server compatibility as well as server/server compatibility \n");
            }
          }
          else {
            changedClassesSb.append("\t\t " + nc.dclass.fullyQualifiedName() + " serialVersionUID was removed, this may break client/server compatibility as well as server/server compatibility \n");
          }
        }
        else {
          if (nc.hasSerialVersionUID) {
            changedClassesSb.append("\t\t " + nc.dclass.fullyQualifiedName() + " serialVersionUID was added \n");
          }
        }
      }
    }
    while (it.hasNext()) {
      newclass = it.next();
      newClassesSb.append(ClassAndVariableDetails.convertForStoring(newclass)).append(": new class\n");
    }
    String result = "";
    if (newClassesSb.length() > newBase) {
      if (changedClassesSb.length() > changedBase) {
        newClassesSb.append("\n");
        newClassesSb.append(changedClassesSb);
      }
      result = newClassesSb.toString();
    } else if (changedClassesSb.length() > changedBase) {
      result = changedClassesSb.toString();
    }
    return result;
  }

  
  
  public static void storeClassesAndVariables(List cams,
      File file) throws IOException {
    FileWriter fw = new FileWriter(file);
    BufferedWriter out = new BufferedWriter(fw);
    for (ClassAndVariables entry: cams) {
      out.append(ClassAndVariableDetails.convertForStoring(entry));
      out.newLine();
    }
    out.flush();
    out.close();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy