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;
+ }
+
+
+
+}