blob: 366d544391ee57ace7f0f7d840dc202d0f67ba96 [file] [log] [blame]
/*
* 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.netbeans.modules.j2ee.ejbcore.util;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.VariableTree;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.Task;
import org.netbeans.api.java.source.TreeMaker;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.modules.j2ee.core.api.support.java.method.MethodModel;
import org.netbeans.modules.j2ee.core.api.support.java.method.MethodModelSupport;
import org.netbeans.modules.j2ee.core.api.support.java.GenerationUtils;
import org.netbeans.modules.j2ee.core.api.support.java.SourceUtils;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.Parameters;
/**
*
* @author Martin Adamek
*/
public final class _RetoucheUtil {
private static final Logger LOG = Logger.getLogger(_RetoucheUtil.class.getName());
private _RetoucheUtil() {}
/** never call this from javac task */
public static String getMainClassName(final FileObject classFO) throws IOException {
JavaSource javaSource = JavaSource.forFileObject(classFO);
final String[] result = new String[1];
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement typeElement = SourceUtils.getPublicTopLevelElement(controller);
if (typeElement != null) {
result[0] = typeElement.getQualifiedName().toString();
}
}
}, true);
return result[0];
}
/**
* @return true if the given <code>javaClass</code> contains a feature
* whose name is identical with the given <code>feature</code>'s name.
*/
public static boolean containsFeature(TypeElement javaClass, Element searchedElement) {
for (Element element : javaClass.getEnclosedElements()) {
if (searchedElement.getSimpleName().equals(element.getSimpleName())) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
private static ElementHandle<TypeElement> getElementHandle(ElementHandle elementHandle) {
if (elementHandle != null && ElementKind.CLASS == elementHandle.getKind()) {
return (ElementHandle<TypeElement>) elementHandle;
}
return null;
}
public static ElementHandle<TypeElement> getJavaClassFromNode(Node node) throws IOException {
ElementHandle<TypeElement> elementHandle = getElementHandle(node.getLookup().lookup(ElementHandle.class));
if (elementHandle != null) {
return elementHandle;
}
//TODO: RETOUCHE TypeElement from Node, this one just takes main TypeElement if ElementHandle is not found
FileObject fileObject = node.getLookup().lookup(FileObject.class);
if (fileObject == null || !fileObject.isValid()) {
return null;
}
JavaSource javaSource = JavaSource.forFileObject(fileObject);
if (javaSource == null) {
return null;
}
final List<ElementHandle<TypeElement>> result = new ArrayList<ElementHandle<TypeElement>>();
try {
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement typeElement = SourceUtils.getPublicTopLevelElement(controller);
if (typeElement != null) {
result.add(ElementHandle.create(typeElement));
}
}
}, true);
} catch (DataObjectNotFoundException donfe) {
// do nothing, node's file was deleted, just return null
}
if (result.size() > 0) {
return result.get(0);
}
return null;
}
public static ExecutableElement getMethodFromNode(Node node) {
//TODO: RETOUCHE ExecutableElement from Node
return null;
}
public static ElementHandle<VariableElement> generateAnnotatedField(FileObject fileObject, final String className, final String annotationType,
final String name, final String fieldType, final Map<String, String> attributes, final boolean isStatic) throws IOException {
JavaSource javaSource = JavaSource.forFileObject(fileObject);
javaSource.runModificationTask(new Task<WorkingCopy>() {
@Override
public void run(WorkingCopy workingCopy) throws IOException {
workingCopy.toPhase(JavaSource.Phase.RESOLVED);
TypeElement typeElement = workingCopy.getElements().getTypeElement(className);
TreeMaker treeMaker = workingCopy.getTreeMaker();
GenerationUtils generationUtils = GenerationUtils.newInstance(workingCopy);
TypeElement returnTypeElement = workingCopy.getElements().getTypeElement(fieldType);
if (returnTypeElement == null) {
LOG.log(Level.WARNING, "TypeElement not found for {0}", fieldType);
}
// modifiers
Set<Modifier> modifiers = new HashSet<>();
modifiers.add(Modifier.PRIVATE);
if (isStatic) {
modifiers.add(Modifier.STATIC);
}
// annotation with attributes
List<ExpressionTree> attributesList = new ArrayList<>();
if (attributes != null) {
for (Map.Entry<String, String> entry : attributes.entrySet()) {
if ("".equals(entry.getKey())) { //NOI18N
attributesList.add(generationUtils.createAnnotationArgument(null, entry.getValue()));
} else {
attributesList.add(generationUtils.createAnnotationArgument(entry.getKey(), entry.getValue()));
}
}
}
AnnotationTree annotationTree = generationUtils.createAnnotation(annotationType, attributesList);
ModifiersTree modifiersTree = treeMaker.addModifiersAnnotation(treeMaker.Modifiers(modifiers), annotationTree);
// field itself
ExpressionTree returnType = returnTypeElement != null ?
treeMaker.QualIdent(returnTypeElement) : treeMaker.QualIdent(fieldType);
VariableTree variableTree = treeMaker.Variable(
modifiersTree,
name,
returnType,
null
);
// adding field to class
ClassTree classTree = workingCopy.getTrees().getTree(typeElement);
ClassTree newClassTree = treeMaker.insertClassMember(classTree, 0, variableTree);
workingCopy.rewrite(classTree, newClassTree);
}
}).commit();
return getFieldHandle(javaSource, className, fieldType, name);
}
public static boolean isInterface(FileObject fileObject, final ElementHandle<TypeElement> elementHandle) throws IOException {
final boolean[] isInterface = new boolean[1];
JavaSource javaSource = JavaSource.forFileObject(fileObject);
if (javaSource == null || elementHandle == null) {
return false;
}
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement typeElement = elementHandle.resolve(controller);
isInterface[0] = ElementKind.INTERFACE == typeElement.getKind();
}
}, true);
return isInterface[0];
}
/**
* Generates unique member name in class-scope
*
* @param fileObject file containing class acting as scope for uniqueness
* @param className name of class acting as scope for uniqueness
* @param memberName suggested member name
* @param defaultValue default value applied if member name cannot be converted to legal Java identifier
* @return given member name if no such member exists or given member name without any illegal characters extended with unique number
*/
public static String uniqueMemberName(FileObject fileObject, final String className, final String memberName, final String defaultValue) throws IOException{
JavaSource javaSource = JavaSource.forFileObject(fileObject);
final String[] result = new String[1];
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
String validName = convertToJavaIdentifier(memberName, defaultValue);
List<String> existingMethodNames = new ArrayList<String>();
TypeElement typeElement = controller.getElements().getTypeElement(className);
if (typeElement == null) {
result[0] = validName;
return;
}
for (Element element : typeElement.getEnclosedElements()) {
existingMethodNames.add(element.getSimpleName().toString());
}
int uniquefier = 1;
String resultName = validName;
while (existingMethodNames.contains(resultName)){
resultName = validName + uniquefier++;
}
result[0] = resultName;
}
}, true);
return result[0];
}
private static String convertToJavaIdentifier(String name, String defaultValue) {
Parameters.notWhitespace("name", name);
Parameters.notWhitespace("defaultValue", defaultValue);
String str = name;
while (str.length() > 0 && !Character.isJavaIdentifierStart(str.charAt(0))) {
str = str.substring(1);
}
StringBuilder result = new StringBuilder();
if (str.length() > 0) {
char firstChar = str.charAt(0);
firstChar = Character.toLowerCase(firstChar);
result.append(firstChar);
for (int i = 1; i < str.length(); i++) {
char character = str.charAt(i);
if (Character.isJavaIdentifierPart(character)) {
result.append(character);
}
}
} else {
result.append(defaultValue);
}
return result.toString();
}
/**
* Tries to find {@link FileObject} which contains class with given className.<br>
* This method will enter javac context for referenceFileObject to find class by its className,
* therefore don't call this method within other javac context.
*
* @param referenceFileObject helper file for entering javac context
* @param className fully-qualified class name to resolve file for
* @return resolved file or null if not found
*/
public static FileObject resolveFileObjectForClass(FileObject referenceFileObject, final String className) throws IOException {
final FileObject[] result = new FileObject[1];
JavaSource javaSource = JavaSource.forFileObject(referenceFileObject);
if (javaSource == null) {
// Should not happen, at least some debug logging, see i.e. issue #202495.
Logger.getLogger(_RetoucheUtil.class.getName()).log(
Level.SEVERE, "JavaSource not created for FileObject: path={0}, valid={1}, mime-type={2}",
new Object[]{
referenceFileObject.getPath(),
referenceFileObject.isValid(),
referenceFileObject.getMIMEType()});
}
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement typeElement = controller.getElements().getTypeElement(className);
if (typeElement != null) {
result[0] = org.netbeans.api.java.source.SourceUtils.getFile(ElementHandle.create(typeElement), controller.getClasspathInfo());
}
}
}, true);
return result[0];
}
//TODO: RETOUCHE move/reuse in SourceUtil, or best - get from java/source!
// package private only for unit test
// see #90968
public static String getTypeName(CompilationController controller, TypeMirror typeMirror) {
TypeKind typeKind = typeMirror.getKind();
switch (typeKind) {
case BOOLEAN : return "boolean"; // NOI18N
case BYTE : return "byte"; // NOI18N
case CHAR : return "char"; // NOI18N
case DOUBLE : return "double"; // NOI18N
case FLOAT : return "float"; // NOI18N
case INT : return "int"; // NOI18N
case LONG : return "long"; // NOI18N
case SHORT : return "short"; // NOI18N
case VOID : return "void"; // NOI18N
case DECLARED :
Element element = controller.getTypes().asElement(typeMirror);
return ((TypeElement) element).getQualifiedName().toString();
case ARRAY :
ArrayType arrayType = (ArrayType) typeMirror;
Element componentTypeElement = controller.getTypes().asElement(arrayType.getComponentType());
return ((TypeElement) componentTypeElement).getQualifiedName().toString() + "[]";
case ERROR :
case EXECUTABLE :
case NONE :
case NULL :
case OTHER :
case PACKAGE :
case TYPEVAR :
case WILDCARD :
default:break;
}
return null;
}
@SuppressWarnings("unchecked")
public static ElementHandle<ExecutableElement> getMethodHandle(JavaSource javaSource, final MethodModel methodModel, final String className) throws IOException {
final ElementHandle[] result = new ElementHandle[1];
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement typeElement = controller.getElements().getTypeElement(className);
for (ExecutableElement method : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
if (MethodModelSupport.isSameMethod(controller, method, methodModel)) {
result[0] = ElementHandle.create(method);
return;
}
}
}
}, true);
return result[0];
}
@SuppressWarnings("unchecked")
public static ElementHandle<VariableElement> getFieldHandle(JavaSource javaSource, final String className, final String fieldType, final String fieldName) throws IOException {
final ElementHandle[] result = new ElementHandle[1];
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement typeElement = controller.getElements().getTypeElement(className);
for (VariableElement variable : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
if (variable.getSimpleName().contentEquals(fieldName) && fieldType.equals(getTypeName(controller, variable.asType()))) {
result[0] = ElementHandle.create(variable);
return;
}
}
}
}, true);
return result[0];
}
public static Set<MethodModel> getMethods(FileObject fileObject, final String className) {
final Set<MethodModel> result = new HashSet<MethodModel>();
JavaSource javaSource = JavaSource.forFileObject(fileObject);
try {
javaSource.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController controller) throws IOException {
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement typeElement = controller.getElements().getTypeElement(className);
if (typeElement != null) {
for (ExecutableElement executableElement : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
MethodModel mm = MethodModelSupport.createMethodModel(controller, executableElement);
if (mm != null){
result.add(mm);
}
}
}
}
}, true);
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
return result;
}
}