You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ej...@apache.org on 2011/10/06 17:47:52 UTC

svn commit: r1179673 [2/3] - in /aries/trunk/versioning: ./ versioning-checker/ versioning-checker/src/ versioning-checker/src/main/ versioning-checker/src/main/java/ versioning-checker/src/main/java/org/ versioning-checker/src/main/java/org/apache/ ve...

Added: aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/ClassDeclaration.java
URL: http://svn.apache.org/viewvc/aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/ClassDeclaration.java?rev=1179673&view=auto
==============================================================================
--- aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/ClassDeclaration.java (added)
+++ aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/ClassDeclaration.java Thu Oct  6 15:47:50 2011
@@ -0,0 +1,568 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.aries.versioning.utils;
+
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Type;
+
+
+public class ClassDeclaration extends GenericDeclaration
+{
+
+  // Binary Compatibility - deletion of package-level access field/method/constructors of classes and interfaces in the package
+  // will not break binary compatibility when an entire package is updated.
+
+  // Assumptions:
+  // 1.  This tool assumes that the deletion of package-level fields/methods/constructors is not break binary compatibility 
+  // based on the assumption of the entire package is updated.
+  // 
+
+
+
+  private final String superName;
+  private final String[] interfaces;
+  private final Map<String, FieldDeclaration> fields;
+  private final Map<String, Set<MethodDeclaration>> methods;
+
+  private final Map<String, Set<MethodDeclaration>> methodsInUpperChain = new HashMap<String, Set<MethodDeclaration>>();
+  private final Map<String, Collection<FieldDeclaration>> fieldsInUpperChain = new HashMap<String, Collection<FieldDeclaration>>();
+  private final Collection<String> supers = new ArrayList<String> ();
+
+
+  private final URLClassLoader jarsLoader;
+
+  private final BinaryCompatibilityStatus binaryCompatible = new BinaryCompatibilityStatus(true, null);
+  public Map<String, FieldDeclaration> getFields()
+  {
+    return fields;
+  }
+  /**
+   * Get the methods in the current class plus the methods in the upper chain
+   * @return
+   */
+  public Map<String, Set<MethodDeclaration>> getAllMethods() {
+
+    Map<String, Set<MethodDeclaration>> methods = new HashMap<String, Set<MethodDeclaration>>(getMethods());
+    methods.putAll(getMethodsInUpperChain());
+    return methods;
+  }
+
+  public Map<String, Set<MethodDeclaration>> getMethods()
+  {
+    return methods;
+  }
+
+
+  public ClassDeclaration(int access, String name, String signature, String superName,
+      String[] interfaces, URLClassLoader loader)
+  {
+    super(access, name, signature);
+    this.superName = superName;
+    this.interfaces = interfaces;
+    this.fields = new HashMap<String, FieldDeclaration>();
+    this.methods = new HashMap<String, Set<MethodDeclaration>>();
+    this.jarsLoader = loader;
+  }
+
+  private void getFieldsRecursively(String superClass) {
+
+    if ((superClass != null) ) {
+      // load the super class of the cd
+      try {
+        SemanticVersioningClassVisitor svc = new SemanticVersioningClassVisitor(jarsLoader);
+        ClassReader cr = new ClassReader(jarsLoader.getResourceAsStream(superClass + SemanticVersioningUtils.classExt));
+        if (cr != null) {
+          cr.accept(svc, 0);
+          ClassDeclaration cd = svc.getClassDeclaration();
+          if (cd != null) {
+            addFieldInUpperChain(cd.getFields());
+            getFieldsRecursively(cd.getSuperName());
+            for (String iface : cd.getInterfaces()) {
+              getFieldsRecursively(iface);
+            }
+          }
+        }
+      } catch (IOException ioe) {
+        // not a problem
+      }
+    }
+  }
+
+  private void getMethodsRecursively(String superClass)
+  {
+    if ((superClass != null) ) {
+      // load the super class of the cd
+      SemanticVersioningClassVisitor svc = new SemanticVersioningClassVisitor(jarsLoader);
+      // use URLClassLoader to load the class
+      try {
+        ClassReader cr = new ClassReader(jarsLoader.getResourceAsStream(superClass  + SemanticVersioningUtils.classExt));
+        if (cr !=  null) {
+          cr.accept(svc, 0);
+          ClassDeclaration cd = svc.getClassDeclaration();
+          if (cd != null) {
+            addMethodsInUpperChain(cd.getMethods());
+            getMethodsRecursively(cd.getSuperName());
+            for (String iface : cd.getInterfaces()) {
+              getMethodsRecursively(iface);
+            }
+          }
+        }
+      } catch (IOException ioe) {
+        // not a deal
+      }
+    }
+  }
+
+
+  public Map<String, Collection<FieldDeclaration>> getFieldsInUpperChain() {
+    if (fieldsInUpperChain.isEmpty()) {
+      getFieldsRecursively(getSuperName());
+      for (String ifs : getInterfaces()) {
+        getFieldsRecursively(ifs);
+      }
+    }
+    return fieldsInUpperChain;
+  }
+
+  private void addFieldInUpperChain(Map<String, FieldDeclaration> fields) {
+    for (Map.Entry<String, FieldDeclaration> field : fields.entrySet()) {
+      String fieldName = field.getKey();
+      Set<FieldDeclaration> fds = new HashSet<FieldDeclaration>();
+      if (fieldsInUpperChain.get(fieldName) != null) {
+        fds.addAll(fieldsInUpperChain.get(fieldName));
+      } 
+
+      fds.add(fields.get(fieldName));
+      fieldsInUpperChain.put(fieldName, fds);
+    }
+  }
+  public Map<String, Set<MethodDeclaration>> getMethodsInUpperChain() {
+    if (methodsInUpperChain.isEmpty()) {
+      getMethodsRecursively(getSuperName());
+      for (String ifs : getInterfaces()) {
+        getMethodsRecursively(ifs);
+      }
+    }
+    return methodsInUpperChain;
+  }
+
+  private void addMethodsInUpperChain(Map<String, Set<MethodDeclaration>> methods) {
+    for (Map.Entry<String, Set<MethodDeclaration>> method : methods.entrySet()) {
+      String methodName = method.getKey();
+      Set<MethodDeclaration> mds = new HashSet<MethodDeclaration>();
+      if (methodsInUpperChain.get(methodName) != null) {
+        mds.addAll(methodsInUpperChain.get(methodName));
+      }
+      mds.addAll(method.getValue());
+      methodsInUpperChain.put(methodName, mds);
+    }
+  }
+  public Collection<String> getUpperChainRecursively(String className) {
+    Collection<String> clazz = new HashSet<String>();
+
+    if (className!= null)  {
+      // load the super class of the cd
+      SemanticVersioningClassVisitor svc = new SemanticVersioningClassVisitor(jarsLoader);
+      try {
+        ClassReader cr = new ClassReader(jarsLoader.getResourceAsStream(className + SemanticVersioningUtils.classExt));
+        cr.accept(svc, 0);
+        clazz.add(className);
+        if (svc.getClassDeclaration() != null) {
+          String superName = svc.getClassDeclaration().getSuperName();
+          className = superName;
+          clazz.addAll(getUpperChainRecursively(superName));
+          if (svc.getClassDeclaration().getInterfaces() != null) {
+            for (String iface : svc.getClassDeclaration().getInterfaces()) {
+              clazz.addAll(getUpperChainRecursively(iface));
+            }
+          }
+        }
+      } catch (IOException ioe) {
+        // not to worry about this. terminate.
+      }
+    }
+    return clazz;
+  }
+
+  public Collection<String> getAllSupers() {
+    if (supers.isEmpty()) {
+      supers.addAll(getUpperChainRecursively(getSuperName()));
+      for (String iface : getInterfaces()) {
+        supers.addAll(getUpperChainRecursively(iface));
+      }
+    }
+    return supers;
+  }
+  public String getSuperName()
+  {
+    return superName;
+  }
+  public String[] getInterfaces()
+  {
+    return interfaces;
+  }
+
+  public void addFields(FieldDeclaration fd) {
+    fields.put(fd.getName(), fd);
+  }
+
+
+  public void addMethods(MethodDeclaration md)
+  {
+    String key = md.getName();
+    Set<MethodDeclaration> overloaddingMethods = methods.get(key);
+    if (overloaddingMethods != null) {
+      overloaddingMethods.add(md);
+      methods.put(key, overloaddingMethods);
+    } else {
+      Set<MethodDeclaration> mds = new HashSet<MethodDeclaration>();
+      mds.add(md);
+      methods.put(key, mds);
+    }
+  }
+
+
+  public BinaryCompatibilityStatus getBinaryCompatibleStatus(ClassDeclaration old) {
+    // check class signature, fields, methods
+    if (old == null) {
+      return binaryCompatible;
+    }
+    BinaryCompatibilityStatus bcs = getClassSignatureBinaryCompatibleStatus(old);
+    if (!!!bcs.isCompatible()) {
+      return bcs;
+    } else {
+      bcs = getAllMethodsBinaryCompatibleStatus(old);
+      if (!!!bcs.isCompatible()) {
+        return bcs;
+      } else {
+        bcs = getFieldsBinaryCompatibleStatus(old);
+        if (!!!bcs.isCompatible()) {
+          return bcs;
+        } else {
+          bcs = areAllSuperPresent(old);
+          if (!!!bcs.isCompatible()) {
+            return bcs;
+          }
+        }
+      }
+    }
+    return binaryCompatible;
+
+  }
+
+
+  public boolean isAbstract() {
+    return Modifier.isAbstract(getAccess());
+  }
+
+  private BinaryCompatibilityStatus getClassSignatureBinaryCompatibleStatus(ClassDeclaration originalClass) {
+    // if a class was not abstract but changed to abstract
+    // not final changed to final
+    // public changed to non-public
+    StringBuilder reason = new StringBuilder("The class "  + getName() );
+    boolean compatible = false;
+    if (!!!originalClass.isAbstract() && isAbstract()) {
+      reason.append(" was not abstract but is changed to be abstract.") ;
+    } else if (!!!originalClass.isFinal() && isFinal()){
+      reason.append( " was not final but is changed to be final.");
+    } else if (originalClass.isPublic() && !!! isPublic()) {
+      reason.append(" was public but is changed to be non-public.");
+    } else {
+      compatible = true;
+    }
+    return new BinaryCompatibilityStatus(compatible, compatible? null: reason.toString());
+  }
+
+  public BinaryCompatibilityStatus getFieldsBinaryCompatibleStatus(ClassDeclaration originalClass) {
+    // for each field to see whether the same field has changed
+    // not final -> final
+    // static <-> nonstatic
+
+    for (Map.Entry<String, FieldDeclaration> entry : originalClass.getFields().entrySet()) {
+
+      FieldDeclaration bef_fd = entry.getValue();
+      FieldDeclaration cur_fd = getFields().get(entry.getKey());
+
+      String fieldName = bef_fd.getName();
+      //only interested in the public or protected fields
+      boolean compatible = true;
+      if (bef_fd.isPublic() || bef_fd.isProtected()) {
+        StringBuilder reason = new StringBuilder("The public or protected field "  +fieldName);
+
+        if (cur_fd == null) {
+          reason.append(" has been deleted.");
+          compatible =  false;
+        } else if ((!!!bef_fd.isFinal()) && (cur_fd.isFinal())) {
+          // make sure it has not been changed to final
+          reason.append(" was not final but has been changed to be final.");
+          compatible = false;
+
+        } else if (bef_fd.isStatic() != cur_fd.isStatic()) {
+          // make sure it the static signature has not been changed
+          reason.append( " was static but is changed to be non static or vice versa.");
+          compatible = false;
+        }
+        // check to see the field type is the same 
+        else if (!!!Type.getType(bef_fd.getDesc()).equals(Type.getType(cur_fd.getDesc()))) {
+          reason.append(" has different type.");
+          compatible = false;
+
+        } else if (SemanticVersioningUtils.isLessAccessible(bef_fd, cur_fd)) {
+          // check whether the new field is less accessible than the old one         
+          reason.append(" is less accessible.");
+          compatible = false;
+        }
+        if (!!!compatible) {
+          return new BinaryCompatibilityStatus(compatible, reason.toString());
+        }
+      }
+    }
+    // need to check the newly added fields do not cause binary compatibility issue:
+    // e.g. the new fields has less access than the old one
+    // the new field is static(instance) while the old one is instance(static) respectively.
+    Collection<String> curFields = getFields().keySet();
+    Collection<String> oldFields = originalClass.getFields().keySet();
+    curFields.removeAll(oldFields);
+    Map<String, Collection<FieldDeclaration>> superFields = new HashMap<String, Collection<FieldDeclaration>>();
+    if (!!!(curFields.isEmpty())) {
+      superFields = getFieldsInUpperChain();
+    }
+    // for each new field we need to find out whether it may cause binary incompatibility
+    for ( String newFieldName : curFields) {
+      // check whether the new field has the same field name in the super with the same type
+      boolean existInSuper = false;
+      if (superFields.containsKey(newFieldName)) {
+        FieldDeclaration newfd = getFields().get(newFieldName);
+        Collection<FieldDeclaration> superfd = superFields.get(newFieldName);
+        FieldDeclaration oldfd = null;
+        if ((superfd != null) ) {
+          for (FieldDeclaration fd : superfd) {
+            if (newfd.equals(fd)) {
+              oldfd = fd;
+              existInSuper = true;
+              break;
+            }
+          }
+        }
+        if ((existInSuper) && ((SemanticVersioningUtils.isLessAccessible(oldfd, newfd)) || (oldfd.isStatic() != newfd.isStatic()))){
+
+          return new BinaryCompatibilityStatus(false, "The new field " + newfd.getName() + " conflicts with the same field in its super class. For more details, check the Binary Compatibility section(Chapter 13) of the Java Specification.");
+        }
+      }
+    }
+    return binaryCompatible;
+  }
+
+  private BinaryCompatibilityStatus getAllMethodsBinaryCompatibleStatus(ClassDeclaration originalClass) {
+    //  for all methods
+    // no methods should have deleted
+    // method return type has not changed
+    // method changed from not abstract -> abstract
+    Map<String, Set<MethodDeclaration>> oldMethods = originalClass.getMethods();
+    Map<String, Set<MethodDeclaration>> newMethods = getMethods();
+    return  areMethodsBinaryCompatible(oldMethods, newMethods) ;
+  }
+
+  public BinaryCompatibilityStatus areMethodsBinaryCompatible(
+      Map<String, Set<MethodDeclaration>> oldMethods, Map<String, Set<MethodDeclaration>> newMethods)
+  {
+
+    Map<String, Collection<MethodDeclaration>> extraMethods = new HashMap<String, Collection<MethodDeclaration>>();
+
+    for (Map.Entry<String, Set<MethodDeclaration>> me : newMethods.entrySet()) {
+      Collection<MethodDeclaration> mds = new ArrayList<MethodDeclaration>(me.getValue());
+      extraMethods.put(me.getKey(), mds);
+    }
+
+    for (Map.Entry<String, Set<MethodDeclaration>> methods : oldMethods.entrySet()) {
+      // all overloading methods, check against the current class
+      String methodName = methods.getKey();
+      Collection<MethodDeclaration> oldMDSigs = methods.getValue();
+      // If the method cannot be found in the current class, it means that it has been deleted.
+      Collection<MethodDeclaration> newMDSigs = newMethods.get(methodName);
+      // for each overloading methods
+      outer: for (MethodDeclaration md : oldMDSigs) {
+        String mdName = md.getName();
+        StringBuilder reason = new StringBuilder("The "  + SemanticVersioningUtils.getReadableMethodSignature(mdName, md.getDesc()) );
+        if (md.isProtected() || md.isPublic()) {
+          if (newMDSigs !=  null) {
+            // try to find it in the current class
+            for (MethodDeclaration new_md : newMDSigs) {
+              // find the method with the same return type, parameter list 
+              if ((md.equals(new_md))) {
+                // If the old method is final but the new one is not or vice versa
+                // If the old method is static but the new one is non static
+                // If the old method is not abstract but the new is
+
+
+                boolean compatible = true;
+                if ( !!!Modifier.isFinal(md.getAccess()) && !!!Modifier.isStatic(md.getAccess()) && Modifier.isFinal(new_md.getAccess())) {
+                  compatible = false;
+                  reason.append(" was not final but has been changed to be final.");
+                } else if (Modifier.isStatic(md.getAccess()) != Modifier.isStatic(new_md.getAccess())){
+                  compatible = false;
+                  reason.append(" has changed from static to non-static or vice versa.");
+                } else if ((Modifier.isAbstract(new_md.getAccess()) == true) && (Modifier.isAbstract(md.getAccess()) == false)) {
+                  compatible = false;
+                  reason.append( " has changed from non abstract to abstract. ");
+                }
+                else if (SemanticVersioningUtils.isLessAccessible(md, new_md)) {
+                  compatible = false;
+                  reason.append(" is less accessible.");
+                }
+                if (!!!compatible) {
+                  return new BinaryCompatibilityStatus(compatible, reason.toString());
+                }
+                else {
+                  // remove from the extra map
+                  Collection<MethodDeclaration> mds = extraMethods.get(methodName);
+                  mds.remove(new_md);
+                  extraMethods.put(methodName, mds);
+                  continue outer;
+                }
+              }
+            }
+          }
+
+          // 
+          // if we are here, it means that we have not found the method with the same description and signature
+          // which means that the method has been deleted. Let's make sure it is not moved to its upper chain.
+          if (!!!isMethodInSuperClass(md)) {
+
+            reason.append(" has been deleted or its return type or parameter list has changed.");
+            return new BinaryCompatibilityStatus(false, reason.toString());
+
+
+          } else {
+            if (newMDSigs != null) {
+              for (MethodDeclaration new_md : newMDSigs) {
+                // find the method with the same return type, parameter list 
+                if ((md.equals(new_md)))  {
+                  Collection<MethodDeclaration> mds = extraMethods.get(methodName);
+                  mds.remove(new_md);
+                  extraMethods.put(methodName, mds);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // Check the newly added method has not caused binary incompatibility
+    for (Map.Entry<String, Collection<MethodDeclaration>> extraMethodSet : extraMethods.entrySet()){
+      for (MethodDeclaration md : extraMethodSet.getValue()) {
+        if (isNewMethodSpecialCase(md)){
+          String reason = "The newly added " + SemanticVersioningUtils.getReadableMethodSignature(md.getName(), md.getDesc()) + " conflicts with the same method in its super class. For more details, check the Binary Compatibility section(Chapter 13) of the Java Specification.";
+          return new BinaryCompatibilityStatus(false, reason);
+        }
+      }
+    }
+
+
+    return  binaryCompatible;
+  }
+  public MethodDeclaration getExtraMethods(ClassDeclaration old ) {
+    // Need to find whether there are new abstract methods added.
+
+    if (Modifier.isAbstract(getAccess())) {
+      Map<String, Set<MethodDeclaration>> currMethodsMap = getAllMethods();
+
+      Map<String, Set<MethodDeclaration>> oldMethodsMap = old.getAllMethods();
+      // only interested in an abstract class
+
+      for (Map.Entry<String, Set<MethodDeclaration>> currMethod : currMethodsMap.entrySet()) {
+        String methodName = currMethod.getKey();
+        Collection<MethodDeclaration> newMethods = currMethod.getValue();
+
+        // for each abstract method, we look for whether it exists in the old class
+        Collection<MethodDeclaration> oldMethods = oldMethodsMap.get(methodName);
+        for (MethodDeclaration new_md : newMethods) {
+          if (oldMethods == null) {
+            return new_md;
+          } else {
+            if (oldMethods.contains(new_md)) {
+              continue;
+            } else {
+              return new_md;
+            }
+          }
+        }
+      }
+      // if we reach here, it means we have scanned all methods in the new classes. All of them are in the original class. No new method is added!
+      return null;
+    }
+    // not to worry as it is not abstract class:o
+    return null;
+  }
+
+  public boolean isMethodInSuperClass(MethodDeclaration md){
+    // scan the super class and interfaces
+    String methodName = md.getName();
+    Collection<MethodDeclaration> overloaddingMethods = getMethodsInUpperChain().get(methodName);
+    if (overloaddingMethods != null) {
+      for (MethodDeclaration value : overloaddingMethods) {
+        // method signature and name same and also the method should not be less accessible
+        if (md.equals(value) && (!!!SemanticVersioningUtils.isLessAccessible(md, value)) && (value.isStatic()==md.isStatic())) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * The newly added method is less accessible than the old one in the super or is a static (respectively instance) method.
+   * @param md
+   * @return
+   */
+  public boolean isNewMethodSpecialCase(MethodDeclaration md){
+    // scan the super class and interfaces
+    String methodName = md.getName();
+    Collection<MethodDeclaration> overloaddingMethods = getMethodsInUpperChain().get(methodName);
+    if (overloaddingMethods != null) {
+      for (MethodDeclaration value : overloaddingMethods) {
+        // method signature and name same and also the method should not be less accessible
+        if (md.equals(value) && (SemanticVersioningUtils.isLessAccessible(md, value) || value.isStatic()!=md.isStatic())) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+  public BinaryCompatibilityStatus areAllSuperPresent(ClassDeclaration old) {
+    Collection<String> oldSupers = old.getAllSupers();
+    boolean containsAll = getAllSupers().containsAll(oldSupers);
+    if (!!!containsAll) {
+      oldSupers.removeAll(getAllSupers());
+      return new BinaryCompatibilityStatus(false, "The superclasses or superinterfaces have stopped being super: " + oldSupers.toString());
+    }
+    return binaryCompatible;
+  }
+}

Added: aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/FieldDeclaration.java
URL: http://svn.apache.org/viewvc/aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/FieldDeclaration.java?rev=1179673&view=auto
==============================================================================
--- aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/FieldDeclaration.java (added)
+++ aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/FieldDeclaration.java Thu Oct  6 15:47:50 2011
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.aries.versioning.utils;
+
+
+public class FieldDeclaration extends GenericDeclaration
+{
+  private String desc;
+  FieldDeclaration(int access, String name, String desc, String signature) {
+    super(access, name, signature);
+    this.desc = desc;
+  }
+
+  public String getDesc()
+  {
+    return desc;
+  }
+
+  @Override
+  public int hashCode()
+  {
+    final int prime = 31;
+    int result = super.hashCode();
+    result = prime * result + ((getName()== null) ? 0 : getName().hashCode());
+    result = prime * result + ((desc == null) ? 0 : desc.hashCode());
+    result = prime * result + ((getSignature() == null) ? 0 : getSignature().hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (this == obj) return true;
+    if (getClass() != obj.getClass()) return false;
+    FieldDeclaration other = (FieldDeclaration) obj;
+    if (getName() == null) {
+      if (other.getName() != null) return false;
+    } else if (!getName().equals(other.getName())) return false;
+    if (desc == null) {
+      if (other.desc != null) return false;
+    } else if (!desc.equals(other.desc)) return false;
+    if (getSignature() == null) {
+      if (other.getSignature() != null) return false;
+    } else if (!getSignature().equals(other.getSignature())) return false;
+    return true;
+  }
+
+}

Added: aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/GenericDeclaration.java
URL: http://svn.apache.org/viewvc/aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/GenericDeclaration.java?rev=1179673&view=auto
==============================================================================
--- aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/GenericDeclaration.java (added)
+++ aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/GenericDeclaration.java Thu Oct  6 15:47:50 2011
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.aries.versioning.utils;
+
+import java.lang.reflect.Modifier;
+
+import org.objectweb.asm.Opcodes;
+
+public abstract class GenericDeclaration
+{
+
+  private final int access;
+  private final String name; 
+  private final String signature;
+  
+  public GenericDeclaration(int access, String name, String signature) {
+    int updatedAccess = access;
+    // Ignore the native or synchronized modifier as they do not affect binary compatibility
+    if (Modifier.isNative(access)) {
+      updatedAccess = updatedAccess - Opcodes.ACC_NATIVE;
+    }
+    if (Modifier.isSynchronized(access)) {
+      updatedAccess = updatedAccess - Opcodes.ACC_SYNCHRONIZED;
+    }
+    this.access = access;
+    this.name = name;
+    this.signature = signature;
+  }
+  public int getAccess()
+  {
+    return access;
+  }
+
+  public String getName()
+  {
+    return name;
+  }
+
+ 
+
+  public String getSignature()
+  {
+    return signature;
+  }
+  
+  public boolean isFinal() {
+    return Modifier.isFinal(access);
+  }
+  
+  public boolean isStatic() {
+    return Modifier.isStatic(access);
+  }
+  
+  public boolean isPublic() {
+    return Modifier.isPublic(access);
+  }
+  
+  public boolean isProtected() {
+    return Modifier.isProtected(access);
+  }
+  public boolean isPrivate() {
+    return Modifier.isPrivate(access);
+  }
+  @Override
+  public int hashCode()
+  {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + access;
+    result = prime * result + ((name == null) ? 0 : name.hashCode());
+    result = prime * result + ((signature == null) ? 0 : signature.hashCode());
+    return result;
+  }
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    GenericDeclaration other = (GenericDeclaration) obj;
+    if (access != other.access) return false;
+    if (name == null) {
+      if (other.name != null) return false;
+    } else if (!name.equals(other.name)) return false;
+    if (signature == null) {
+      if (other.signature != null) return false;
+    } else if (!signature.equals(other.signature)) return false;
+    return true;
+  }
+
+}
\ No newline at end of file

Added: aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/MethodDeclaration.java
URL: http://svn.apache.org/viewvc/aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/MethodDeclaration.java?rev=1179673&view=auto
==============================================================================
--- aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/MethodDeclaration.java (added)
+++ aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/MethodDeclaration.java Thu Oct  6 15:47:50 2011
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.aries.versioning.utils;
+
+import java.lang.reflect.Modifier;
+
+
+public class MethodDeclaration extends GenericDeclaration
+{
+  private final String desc;
+  MethodDeclaration(int access, String name, String desc, String signature, String[] exceptions) {
+  super(access, name, signature);
+    this.desc = desc;
+  }
+  public String getDesc()
+  {
+    return desc;
+  }
+
+  public boolean isAbstract() {
+    return Modifier.isAbstract(getAccess());
+  }
+  @Override
+  public int hashCode()
+  {
+    final int prime = 31;
+    int result = super.hashCode();
+    result = prime * result + ((desc == null) ? 0 : desc.hashCode());
+    result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
+    result = prime * result + ((getSignature() == null) ? 0 : getSignature().hashCode());
+    return result;
+  }
+
+
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (this == obj) return true;
+    if (getClass() != obj.getClass()) return false;
+    MethodDeclaration other = (MethodDeclaration) obj;
+    if (desc == null) {
+      if (other.desc != null) return false;
+    } else if (!desc.equals(other.desc)) return false;
+    if (getName() == null) {
+      if (other.getName() != null) return false;
+    } else if (!getName().equals(other.getName())) return false;
+    if (getSignature() == null) {
+      if (other.getSignature() != null) return false;
+    } else if (!getSignature().equals(other.getSignature())) return false;
+    return true;
+  }
+
+}

Added: aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningClassVisitor.java
URL: http://svn.apache.org/viewvc/aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningClassVisitor.java?rev=1179673&view=auto
==============================================================================
--- aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningClassVisitor.java (added)
+++ aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningClassVisitor.java Thu Oct  6 15:47:50 2011
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.aries.versioning.utils;
+
+import java.lang.reflect.Modifier;
+import java.net.URLClassLoader;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+public class SemanticVersioningClassVisitor implements ClassVisitor
+{
+
+  private ClassDeclaration classDeclaration;
+  private boolean needVisit = false;
+  private URLClassLoader loader = null;
+  public SemanticVersioningClassVisitor(URLClassLoader newJarLoader) {
+    this.loader = newJarLoader;
+  }
+
+  public ClassDeclaration getClassDeclaration()
+  {
+    return classDeclaration;
+  }
+  /*
+   * (non-Javadoc)
+   * 
+   * @see org.objectweb.asm.ClassAdapter#visit(int, int,
+   * java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+   */
+  // visit the header of the class
+  public void  visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+    // only interested in public class
+    if (Modifier.isPublic(access) || (Modifier.isProtected(access))) {
+    classDeclaration = new ClassDeclaration(access, name, signature, superName, interfaces, loader);
+    needVisit = true;
+    
+    } 
+    }
+  /*
+   * (non-Javadoc)
+   * 
+   * @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String,
+   * java.lang.String, java.lang.String, java.lang.Object)
+   * 
+   * Grab all protected or public fields
+   */
+  @Override
+  public FieldVisitor visitField(int access, String name, String desc,
+      String signature, Object value) {
+    if (needVisit) {
+      FieldDeclaration fd = new FieldDeclaration(access, name, desc, signature);
+
+      classDeclaration.addFields(fd);
+    }
+    return null;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String,
+   * java.lang.String, java.lang.String, java.lang.String[])
+   * Get all non-private methods
+   */
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+
+
+    if (needVisit) {
+      MethodDeclaration md = new MethodDeclaration(access, name, desc, signature, exceptions);
+      classDeclaration.addMethods(md);
+    }
+    return null;
+  }
+  @Override
+  public AnnotationVisitor visitAnnotation(String arg0, boolean arg1)
+  {
+    return null;
+  }
+  @Override
+  public void visitAttribute(Attribute arg0)
+  {
+    // no-op    
+  }
+ 
+  @Override
+  public void visitEnd()
+  {
+  //no-op
+    
+  }
+  @Override
+  public void visitInnerClass(String name, String outerName, String innerName, int access)
+  {
+    //no-op
+    //The inner class will be scanned on its own. However, the method level class will be excluded, as they won't be public or protected.
+   
+  }
+  @Override
+  public void visitOuterClass(String owner, String name, String desc)
+  {
+  //no op
+    
+  }
+  @Override
+  public void visitSource(String arg0, String arg1)
+  {
+    //no-op
+    
+  }
+
+
+}

Added: aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningUtils.java
URL: http://svn.apache.org/viewvc/aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningUtils.java?rev=1179673&view=auto
==============================================================================
--- aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningUtils.java (added)
+++ aries/trunk/versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/SemanticVersioningUtils.java Thu Oct  6 15:47:50 2011
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.aries.versioning.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class SemanticVersioningUtils
+{
+
+  public static String classExt = ".class";
+  public static String javaExt = ".java";
+  public static String schemaExt = ".xsd";
+  public static String jarExt = ".jar";
+
+  public static boolean isLessAccessible(GenericDeclaration before, GenericDeclaration after) {
+
+    if (before.getAccess() == after.getAccess()) {
+      return false;
+    }
+    //When it reaches here, the two access are different. Let's make sure the whether the after field has less access than the before field.
+    if (before.isPublic() ){
+      if (!!!after.isPublic()) {
+        return true;
+      } 
+    } else if (before.isProtected()) {
+      if (!!!(after.isPublic() || after.isProtected())) {
+        return true;
+      }
+    } else {
+      if (!!!before.isPrivate()) {
+        // the field is package level.
+        if (after.isPrivate()) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Transform ASM method desc to a human readable form
+   * e.g.
+   * void m(int i, float f) <= (IF)V
+   * int m(Object o) <= (Ljava/lang/Object;)I
+   * int[] m(int i, String s) <= (ILjava/lang/String;)[I
+   * Object m(int[] i) <= ([I)Ljava/lang/Object;
+   * @param methodName method name
+   * @param methodDesc binary form method description
+   * @return
+   */
+  public static String getReadableMethodSignature( String methodName, String methodDesc) {
+    // need to find the return type first, which is outside the ()
+    int lastBrace = methodDesc.lastIndexOf(")");
+    String constructorSymbol = "<init>";
+    // parameter
+    StringBuilder methodSignature = new StringBuilder();
+    if (lastBrace == -1) {
+      // This is odd, don't attempt to transform. Just return back. Won't happen unless byte code weaving is not behaving.
+      return "method " + methodName + methodDesc;
+    }
+    String param = methodDesc.substring(1, lastBrace);
+    if (constructorSymbol.equals(methodName)) {
+      //This means the method is a constructor. In the binary form, the constructor carries a name 'init'. Let's use the source
+      // code proper name
+      methodSignature.append("constructor with parameter list ");
+    } else {
+      String returnType = methodDesc.substring(lastBrace + 1);
+      methodSignature.append("method ");
+      methodSignature.append(transform(returnType));
+      methodSignature.append(" ");
+      methodSignature.append(methodName);
+    }
+    // add the paramether list
+    methodSignature.append("(");
+    methodSignature.append(transform(param));
+    methodSignature.append(")");
+    return methodSignature.toString();
+  }
+
+  /**
+   * ASM Type descriptor look up table
+   * @author emily
+   *
+   */
+  private enum TypeDescriptor{
+    I("int"), Z("boolean"),  C("char"), B("byte"), 
+    S("short"), F("float"), J("long"), D("double"), V("void");
+
+    String desc;
+    TypeDescriptor(String desc) {
+      this.desc = desc;
+    }
+    String getDesc(){
+      return desc;
+    }
+    private static final Map<String, TypeDescriptor> stringToEnum = new HashMap<String, TypeDescriptor>();
+    static {
+      for (TypeDescriptor td : values()) {
+        stringToEnum.put(td.toString(), td);
+      }
+    }
+    public static TypeDescriptor fromString(String symbol) {
+      return stringToEnum.get(symbol);
+    }
+
+  }
+  private static String transform(String asmDesc)
+  {
+    String separator = ", ";
+    int brkCount = 0;
+    StringBuilder returnStr = new StringBuilder();
+    //remove the '['s
+
+    while (asmDesc.length() > 0) {
+      while(asmDesc.startsWith("[")){
+        asmDesc = asmDesc.substring(1);
+        brkCount ++;
+      }
+      while (asmDesc.startsWith("L")) {
+        //remove the L and ;
+        int semiColonIndex = asmDesc.indexOf(";");
+
+
+
+        returnStr.append(asmDesc.substring(1, semiColonIndex));
+        asmDesc = asmDesc.substring(semiColonIndex + 1);
+        for (int index =0; index < brkCount; index ++) {
+          returnStr.append("[]");
+        }
+        brkCount =0;
+        returnStr.append(separator);
+      }
+
+      TypeDescriptor td = null;
+      while ( (asmDesc.length() > 0) && ( td = TypeDescriptor.fromString(asmDesc.substring(0,1))) != null) {
+
+        returnStr.append(td.getDesc());
+        for (int index =0; index < brkCount; index ++) {
+          returnStr.append("[]");
+        }
+        brkCount =0;
+        returnStr.append(separator);
+        asmDesc = asmDesc.substring(1);
+      } 
+
+
+    }
+    String finalStr = returnStr.toString();
+    if (finalStr.endsWith(separator)){
+      finalStr = finalStr.substring(0, finalStr.lastIndexOf(separator));
+    }
+    return finalStr;
+  }
+
+
+
+}