blob: 6a20f576e8d43558b9a86225b42318c7ad5ac854 [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.micronaut;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.java.source.ClassIndex;
import org.netbeans.api.java.source.ClasspathInfo;
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.TypeUtilities;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.modules.parsing.api.ParserManager;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.modules.parsing.api.Source;
import org.netbeans.modules.parsing.api.UserTask;
import org.netbeans.modules.parsing.spi.ParseException;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup;
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
/**
*
* @author Dusan Balek
*/
public final class MicronautConfigProperties {
private static final String CONFIG_METADATA_JSON = "META-INF/spring-configuration-metadata.json";
private static final ElementHandle<TypeElement> PROPERTY_HANDLE = ElementHandle.createTypeElementHandle(ElementKind.ANNOTATION_TYPE, "io.micronaut.context.annotation.Property");
private MicronautConfigProperties() {
}
public static boolean hasConfigMetadata(Project project) {
ClassPath cp = getExecuteClasspath(project);
return cp != null && !cp.findAllResources(CONFIG_METADATA_JSON).isEmpty();
}
public static Map<String, ConfigurationMetadataProperty> getProperties(Project project) {
Map<String, ConfigurationMetadataProperty> props = new LinkedHashMap<>();
ClassPath cp = getExecuteClasspath(project);
if (cp != null) {
for (FileObject fo : cp.findAllResources(CONFIG_METADATA_JSON)) {
try {
ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(fo.getInputStream()).build();
props.putAll(repository.getAllProperties());
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
String customRepository = getCustomRepository(project);
if (customRepository != null) {
try {
InputStream stream = new ByteArrayInputStream(customRepository.getBytes(StandardCharsets.UTF_8));
ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(stream).build();
props.putAll(repository.getAllProperties());
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
}
return props;
}
public static Map<String, ConfigurationMetadataGroup> getGroups(Project project) {
Map<String, ConfigurationMetadataGroup> groups = new LinkedHashMap<>();
ClassPath cp = getExecuteClasspath(project);
if (cp != null) {
for (FileObject fo : cp.findAllResources(CONFIG_METADATA_JSON)) {
try {
ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(fo.getInputStream()).build();
groups.putAll(repository.getAllGroups());
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
String customRepository = getCustomRepository(project);
if (customRepository != null) {
try {
InputStream stream = new ByteArrayInputStream(customRepository.getBytes(StandardCharsets.UTF_8));
ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(stream).build();
groups.putAll(repository.getAllGroups());
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
}
return groups;
}
private static String getCustomRepository(Project project) {
AtomicReference<String> ret = new AtomicReference<>();
SourceGroup[] srcGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
for (int i = 0; i < srcGroups.length; i++) {
SourceGroup srcGroup = srcGroups[i];
ClassIndex ci = ClasspathInfo.create(srcGroup.getRootFolder()).getClassIndex();
Set<FileObject> resources = ci.getResources(PROPERTY_HANDLE,
EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES), EnumSet.of(ClassIndex.SearchScope.SOURCE));
try {
ParserManager.parse(resources.stream().map(resource -> Source.create(resource)).collect(Collectors.toList()), new UserTask() {
@Override
public void run(ResultIterator resultIterator) throws Exception {
CompilationController cc = CompilationController.get(resultIterator.getParserResult());
cc.toPhase(JavaSource.Phase.RESOLVED);
Trees trees = cc.getTrees();
TypeElement te = PROPERTY_HANDLE.resolve(cc);
new TreePathScanner<Void, Void>() {
@Override
public Void visitAnnotation(AnnotationTree node, Void p) {
if (te == trees.getElement(new TreePath(getCurrentPath(), node.getAnnotationType()))) {
for (ExpressionTree argument : node.getArguments()) {
if (argument.getKind() == Tree.Kind.ASSIGNMENT) {
ExpressionTree variable = ((AssignmentTree) argument).getVariable();
ExpressionTree expression = ((AssignmentTree) argument).getExpression();
if (expression.getKind() == Tree.Kind.STRING_LITERAL && variable.getKind() == Tree.Kind.IDENTIFIER && "name".contentEquals(((IdentifierTree) variable).getName())) {
try {
String value = (String) ((LiteralTree) expression).getValue();
JSONObject group = new JSONObject();
JSONObject property = new JSONObject();
group.put("name", value);
property.put("name", value);
final TreePath grandParentPath = getCurrentPath().getParentPath().getParentPath();
if (grandParentPath.getLeaf().getKind() == Tree.Kind.VARIABLE) {
Element el = trees.getElement(grandParentPath);
if (el != null && el.getKind().isField()) {
String typeName = cc.getTypeUtilities().getTypeName(el.asType(), TypeUtilities.TypeNameOptions.PRINT_FQN).toString();
String sourceTypeName = cc.getElementUtilities().getElementName(el.getEnclosingElement(), true).toString();
property.put("type", typeName);
property.put("sourceType", sourceTypeName);
group.put("type", sourceTypeName);
}
}
JSONArray groups = new JSONArray();
groups.put(group);
JSONArray properties = new JSONArray();
properties.put(property);
JSONObject data = new JSONObject();
data.put("groups", groups);
data.put("properties", properties);
ret.set(data.toString());
} catch (JSONException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}
return super.visitAnnotation(node, p);
}
}.scan(new TreePath(cc.getCompilationUnit()), null);
}
});
} catch (ParseException ex) {
Exceptions.printStackTrace(ex);
}
}
return ret.get();
}
private static ClassPath getExecuteClasspath(Project project) {
SourceGroup[] srcGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
if (srcGroups.length > 0) {
return ClassPath.getClassPath(srcGroups[0].getRootFolder(), ClassPath.EXECUTE);
}
return null;
}
}