/*
* 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.Collection;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.Opcodes;
public class SemanticVersioningUtils {
public static final String classExt = ".class";
public static final String javaExt = ".java";
public static final String schemaExt = ".xsd";
public static final String jarExt = ".jar";
public static final String CONSTRUTOR = "<init>";
public static final String MAJOR_CHANGE = "major";
public static final String MINOR_CHANGE = "minor";
public static final String NO_CHANGE = "no";
public static final String oneLineBreak = "\r\n";
public static final String twoLineBreaks = oneLineBreak + oneLineBreak;
public static final String PROPERTY_FILE_IDENTIFIER = "java/util/ListResourceBundle";
public static final String CLINIT = "<clinit>";
public static final String SERIALIZABLE_CLASS_IDENTIFIER = "java/io/Serializable";
public static final String SERIAL_VERSION_UTD = "serialVersionUID";
public static final String ENUM_CLASS = "java/lang/Enum";
public static final int ASM4 = Opcodes.ASM4;
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;
}
/**
* 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);
}
}
/**
* Transform ASM method desc to a human readable form
* Method declaration in source file Method descriptor
* <pre>
* 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;
* </pre>
*/
public static String getReadableMethodSignature(String methodName, String methodDesc) {
// need to find the return type first, which is outside the ()
int lastBrace = methodDesc.lastIndexOf(")");
// 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 (CONSTRUTOR.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();
}
public 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(";");
if (semiColonIndex == -1) {
//This is odd. The asm binary code is invalid. Do not attempt to transform.
return asmDesc;
}
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));
}
//replace "/" with "." as bytecode uses / in the package names
finalStr = finalStr.replaceAll("/", ".");
return finalStr;
}
/**
* Return whether the binary is property file. If the binary implements the interface of java.util.ListResourceBundle
*
* @param cd
* @return
*/
public static boolean isPropertyFile(ClassDeclaration cd) {
Collection<String> supers = cd.getAllSupers();
return (supers.contains(PROPERTY_FILE_IDENTIFIER));
}
}