/*******************************************************************************
* Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package de.gebit.integrity.ui.utils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
/**
* Utility class providing various helper functions. This one performs Eclipse JDT stuff.
*
* @author Rene Schneider - initial API and implementation
*
*/
public final class IntegrityDSLUIUtil {
private IntegrityDSLUIUtil() {
// private constructor
}
/**
* Determines the fully qualified class name from a type signature and a type where that type signature is declared.
*
* @param aTypeSignature
* the type signature
* @param aDeclaringType
* the type in which the type signature was found
* @return the fully qualified class name, packaged up with generics parameter information
* @throws JavaModelException
*/
public static ResolvedTypeName getResolvedTypeName(String aTypeSignature, IType aDeclaringType)
throws JavaModelException {
int tempArrayCount = Signature.getArrayCount(aTypeSignature);
char tempType = aTypeSignature.charAt(tempArrayCount);
if (tempType == Signature.C_UNRESOLVED) {
String tempName = "";
int tempBracketOpenPosition = aTypeSignature.indexOf(Signature.C_GENERIC_START, tempArrayCount + 1);
if (tempBracketOpenPosition > 0) {
tempName = aTypeSignature.substring(tempArrayCount + 1, tempBracketOpenPosition);
} else {
int tempSemicolonPosition = aTypeSignature.indexOf(Signature.C_SEMICOLON, tempArrayCount + 1);
if (tempSemicolonPosition == -1) {
throw new IllegalArgumentException();
}
tempName = aTypeSignature.substring(tempArrayCount + 1, tempSemicolonPosition);
}
String[][] tempResolvedNames = aDeclaringType.resolveType(tempName);
if (tempResolvedNames != null && tempResolvedNames.length > 0) {
StringBuffer tempBuffer = new StringBuffer();
if (tempResolvedNames[0][0] != null && tempResolvedNames[0][0].length() > 0) {
tempBuffer.append(tempResolvedNames[0][0]);
}
if (tempResolvedNames[0][1] != null && tempResolvedNames[0][1].length() > 0) {
if (tempBuffer.length() > 0) {
tempBuffer.append('.');
}
tempBuffer.append(tempResolvedNames[0][1]);
}
String tempRawTypeName = tempBuffer.toString();
// Now on to the generic parameters
if (tempBracketOpenPosition > 0) {
int tempBracketClosePosition = aTypeSignature.lastIndexOf(Signature.C_GENERIC_END);
if (tempBracketClosePosition == -1) {
throw new IllegalArgumentException();
}
String tempParameterNamesSignature = aTypeSignature.substring(tempBracketOpenPosition + 1,
tempBracketClosePosition);
// This one must be splitted now
List<String> tempParameterNameSignatures = new ArrayList<String>();
int tempGenericStackSize = 0;
int tempStart = 0;
for (int i = 0; i < tempParameterNamesSignature.length(); i++) {
char tempChar = tempParameterNamesSignature.charAt(i);
if (tempChar == Signature.C_GENERIC_START) {
tempGenericStackSize++;
} else if (tempChar == Signature.C_GENERIC_END) {
tempGenericStackSize--;
} else if (tempChar == Signature.C_SEMICOLON) {
if (tempGenericStackSize == 0) {
// this is a valid splitting point
tempParameterNameSignatures
.add(tempParameterNamesSignature.substring(tempStart, i + 1));
tempStart = i + 1;
}
}
}
ResolvedTypeName[] tempGenericParameterTypes = new ResolvedTypeName[tempParameterNameSignatures
.size()];
for (int i = 0; i < tempParameterNameSignatures.size(); i++) {
tempGenericParameterTypes[i] = getResolvedTypeName(tempParameterNameSignatures.get(i),
aDeclaringType);
}
return new ResolvedTypeName(tempRawTypeName, tempGenericParameterTypes);
} else {
return new ResolvedTypeName(tempRawTypeName);
}
}
return null;
} else {
return new ResolvedTypeName(Signature.toString(aTypeSignature.substring(tempArrayCount)).replace('/', '.'));
}
}
/**
* Holds a resolved type name, as returned by {@link IntegrityDSLUIUtil#getResolvedTypeName(String, IType)}.
*
*
* @author Rene Schneider - initial API and implementation
*
*/
public static class ResolvedTypeName {
/**
* The raw type name.
*/
private String rawType;
/**
* The generic parameters, if present.
*/
private ResolvedTypeName[] genericParameterTypes;
/**
* Creates a new instance.
*
* @param aRawType
*/
public ResolvedTypeName(String aRawType) {
rawType = aRawType;
}
/**
* Creates a new instance.
*
* @param aRawType
* @param someGenericParameterTypes
*/
public ResolvedTypeName(String aRawType, ResolvedTypeName[] someGenericParameterTypes) {
rawType = aRawType;
genericParameterTypes = someGenericParameterTypes;
}
public String getRawType() {
return rawType;
}
public ResolvedTypeName[] getGenericParameterTypes() {
return genericParameterTypes;
}
}
/**
* Finds an {@link IType} by its corresponding fully qualified name.
*
* @param aFullyQualifiedClassName
* the fully qualified name
* @return the type or null if not found
*/
public static IType findTypeByName(String aFullyQualifiedClassName) {
if (aFullyQualifiedClassName == null) {
return null;
}
return new TypeFinder().findTypeByName(aFullyQualifiedClassName);
}
/**
* Finds an {@link IField} by its name in a given {@link IType}.
*
* @param aType
* the type to search in
* @param aFieldName
* the field name
* @return the field or null if not found
* @throws JavaModelException
*/
public static IField findFieldByName(IType aType, String aFieldName) throws JavaModelException {
ITypeHierarchy tempTypeHierarchy = aType.newSupertypeHierarchy(null);
IType tempTypeInFocus = aType;
// try to locate field by its original name
while (tempTypeInFocus != null) {
IField tempField = tempTypeInFocus.getField(aFieldName);
if (tempField != null && tempField.exists()) {
return tempField;
} else {
tempTypeInFocus = tempTypeHierarchy.getSuperclass(tempTypeInFocus);
}
}
// if not locatable - try find with custom prefix
tempTypeInFocus = aType;
for (String tempPrefix : CUSTOM_PREFIX_LIST) {
String tempNameWithPrefix = tempPrefix + aFieldName.substring(0, 1).toUpperCase() + aFieldName.substring(1);
while (tempTypeInFocus != null) {
IField tempField = tempTypeInFocus.getField(tempNameWithPrefix);
if (tempField != null && tempField.exists()) {
return tempField;
} else {
tempTypeInFocus = tempTypeHierarchy.getSuperclass(tempTypeInFocus);
}
}
}
return null;
}
/**
* List of potential prefixes that may be placed before the "actual" field name (the one visible from the outside by
* looking at the accessor names) in order to form the internally used field name.
*/
private static final String[] CUSTOM_PREFIX_LIST = new String[] { "Local" };
/**
* Finds all {@link IField}s in a given {@link IType} (including fields defined in supertypes!).
*
* @param aType
* the type to search in
* @param aFilterSetterlessPrivateFields
* whether setterless private fields shall be filtered out
* @return all fields
* @throws JavaModelException
*/
public static List<FieldDescription> getAllFields(IType aType, boolean aFilterSetterlessPrivateFields)
throws JavaModelException {
ITypeHierarchy tempTypeHierarchy = aType.newSupertypeHierarchy(null);
IType tempTypeInFocus = aType;
List<FieldDescription> tempResults = new ArrayList<FieldDescription>();
while (tempTypeInFocus != null) {
Set<String> tempSetterMethodNames = null;
for (IField tempField : tempTypeInFocus.getFields()) {
FieldDescription tempFieldDescription = new FieldDescription(tempField.getElementName(),
JavadocUtil.getFieldJavadoc(tempField));
if (aFilterSetterlessPrivateFields) {
boolean tempIsReachable = false;
if (Flags.isPublic(tempField.getFlags())) {
tempIsReachable = true;
} else {
// We are doing the bean introspection by ourself because we don't want to load the class, which
// would be necessary for using the java.bean.* stuff.
if (tempSetterMethodNames == null) {
tempSetterMethodNames = new HashSet<String>();
for (IMethod tempMethod : tempTypeInFocus.getMethods()) {
String tempMethodName = tempMethod.getElementName();
if (tempMethodName.startsWith("set")) {
tempSetterMethodNames.add(tempMethodName);
}
}
}
String tempSetterMethodNameSuffix = tempField.getElementName().substring(0, 1).toUpperCase()
+ tempField.getElementName().substring(1);
// first check for the default setter name
if (tempSetterMethodNames.contains("set" + tempSetterMethodNameSuffix)) {
tempIsReachable = true;
} else {
// The field may be prefixed (eg. "localText" as field name with "setText" and "getText" as
// accessors)
for (String tempCustomPrefix : CUSTOM_PREFIX_LIST) {
String tempCapitalizedCustomPrefix = tempCustomPrefix.substring(0, 1).toUpperCase()
+ tempCustomPrefix.substring(1);
if (tempSetterMethodNameSuffix.startsWith(tempCapitalizedCustomPrefix)
&& tempCustomPrefix.length() < tempSetterMethodNameSuffix.length()) {
// Okay, this prefix may be a match. But we still need to see if there's a matching
// setter WITHOUT the prefix.
String tempUnprefixedFieldName = tempSetterMethodNameSuffix
.substring(tempCustomPrefix.length());
if (tempSetterMethodNames.contains("set" + tempUnprefixedFieldName)) {
// Good, there is. We will use the field name without the prefix to refer to the
// field, as that's the name under which this field is visible to the outside
// world via the accessors.
tempIsReachable = true;
tempFieldDescription.setFieldName(tempUnprefixedFieldName.substring(0, 1)
.toLowerCase() + tempUnprefixedFieldName.substring(1));
break;
}
}
}
}
}
if (!tempIsReachable) {
continue;
}
}
tempResults.add(tempFieldDescription);
}
tempTypeInFocus = tempTypeHierarchy.getSuperclass(tempTypeInFocus);
}
return tempResults;
}
/**
* Finds providers linked to fixtures using link annotations.
*
*
* @author Rene Schneider - initial API and implementation
*
* @param <Provider>
*/
private static final class TypeFinder {
/**
* The search result.
*/
private IType searchResult;
private IType findTypeByName(final String aFullyQualifiedName) {
SearchPattern tempInheritPattern = SearchPattern.createPattern(aFullyQualifiedName.replace('$', '.'),
IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH
| SearchPattern.R_CASE_SENSITIVE);
IJavaSearchScope tempScope = SearchEngine.createWorkspaceScope();
SearchRequestor tempRequestor = new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch aMatch) throws CoreException {
searchResult = (IType) aMatch.getElement();
}
};
SearchEngine tempSearchEngine = new SearchEngine();
try {
tempSearchEngine.search(tempInheritPattern,
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, tempScope,
tempRequestor, null);
} catch (CoreException exc) {
exc.printStackTrace();
}
return searchResult;
}
}
/**
* Encapsulates data about a field in a Java Bean Type.
*
*
* @author Rene Schneider - initial API and implementation
*
*/
public static class FieldDescription {
/**
* The name of the field.
*/
private String fieldName;
/**
* The JavaDoc comment for the field.
*/
private String javaDoc;
/**
* Creates a new instance.
*
* @param aFieldname
* the name of the field
* @param aFieldJavadoc
* the javadoc comment of the field
*/
public FieldDescription(String aFieldname, String aFieldJavadoc) {
fieldName = aFieldname;
javaDoc = aFieldJavadoc;
}
public String getJavaDoc() {
return javaDoc;
}
public void setJavaDoc(String aJavaDoc) {
this.javaDoc = aJavaDoc;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String aFieldName) {
this.fieldName = aFieldName;
}
}
}