blob: b319908fb9673c032dfd281440db8b75e6035ef2 [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.jakarta.web.beans.navigation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.modules.j2ee.metadata.model.api.MetadataModel;
import org.netbeans.modules.j2ee.metadata.model.api.MetadataModelAction;
import org.netbeans.modules.j2ee.metadata.model.api.MetadataModelException;
import org.netbeans.modules.jakarta.web.beans.api.model.DependencyInjectionResult;
import org.netbeans.modules.jakarta.web.beans.api.model.WebBeansModel;
import org.openide.filesystems.FileObject;
/**
* @author ads
*/
public final class InjectablesModel extends DefaultTreeModel
implements JavaHierarchyModel
{
private static final long serialVersionUID = -6845959436250662000L;
private static final Logger LOG = Logger.getLogger(
InjectablesModel.class.getName());
static Element[] EMPTY_ELEMENTS_ARRAY = new Element[0];
public InjectablesModel(DependencyInjectionResult result,
CompilationController controller ,MetadataModel<WebBeansModel> model )
{
super(null);
myModel = model;
if ( result.getKind() == DependencyInjectionResult.ResultKind.DEFINITION_ERROR ||
!( result instanceof DependencyInjectionResult.ApplicableResult))
{
myTypeHandles= Collections.emptyList();
myProductionHandles = Collections.emptyMap();
return;
}
DependencyInjectionResult.ApplicableResult applicableResult =
(DependencyInjectionResult.ApplicableResult) result;
Set<TypeElement> typeElements = applicableResult.getTypeElements();
myProductionHandles = new HashMap<ElementHandle<?>,
ElementHandle<TypeElement>>();
myDisabledBeans = new HashSet<ElementHandle<?>>();
Set<Element> disabled = new HashSet<Element>();
myTypeHandles = new ArrayList<ElementHandle<TypeElement>>(typeElements.size());
for (TypeElement el : typeElements) {
ElementHandle<TypeElement> handle = ElementHandle.create(el);
myTypeHandles.add(handle);
if ( applicableResult.isDisabled(el)){
myDisabledBeans.add( handle );
disabled.add( el );
}
}
Set<Element> productions = applicableResult.getProductions();
Map<Element,TypeElement> productionMap = new HashMap<Element, TypeElement>();
for (Element production : productions) {
ElementHandle<Element> handleKey = ElementHandle.create( production );
TypeElement clazz = controller.getElementUtilities().
enclosingTypeElement(production);
myProductionHandles.put(handleKey, ElementHandle.create( clazz ) );
productionMap.put( production, clazz );
if ( applicableResult.isDisabled(production)){
myDisabledBeans.add( handleKey );
disabled.add( production );
}
}
update( typeElements, productionMap, disabled , controller);
}
@Override
public void update() {
update( myTypeHandles , myProductionHandles );
}
@Override
public void fireTreeNodesChanged() {
super.fireTreeNodesChanged(this, getPathToRoot((TreeNode)getRoot()),
null, null);
}
private void update( final List<ElementHandle<TypeElement>> typeHandles ,
final Map<ElementHandle<?>,ElementHandle<TypeElement>>
productions )
{
try {
getModel().runReadAction(
new MetadataModelAction<WebBeansModel, Void>() {
@Override
public Void run( WebBeansModel model ) {
Set<Element> disabled = new HashSet<Element>();
List<TypeElement> typesList = fillTypes(typeHandles,
model, disabled);
Map<Element, TypeElement> productionsMap =
fillProductions( productions, model , disabled);
update(typesList, productionsMap , disabled , model
.getCompilationController());
return null;
}
});
return;
}
catch (MetadataModelException e) {
LOG.log(Level.WARNING, e.getMessage(), e);
}
catch (IOException e) {
LOG.log(Level.WARNING, e.getMessage(), e);
}
}
private Map<Element, TypeElement> fillProductions(
Map<ElementHandle<?>, ElementHandle<TypeElement>> productions,
WebBeansModel model, Set<Element> disabled )
{
Map<Element, TypeElement> result;
if ( productions == null || productions.size() == 0){
result = Collections.emptyMap();
}
else {
result = new HashMap<Element, TypeElement>();
for(Entry<ElementHandle<?>,ElementHandle<TypeElement>>
entry : productions.entrySet() )
{
ElementHandle<?> handle = entry.getKey();
Element element = handle.resolve(model.getCompilationController());
if (element != null) {
if (myDisabledBeans.contains(handle)) {
disabled.add(element);
}
result.put(element, entry.getValue().resolve(
model.getCompilationController()));
}
else {
LOG.warning(handle.toString()
+ " cannot be resolved using: " // NOI18N
+ model.getCompilationController()
.getClasspathInfo());
}
}
}
return result;
}
private List<TypeElement> fillTypes(final List<ElementHandle<TypeElement>>
typeHandles, WebBeansModel model, Set<Element> disabled )
{
List<TypeElement> typesList;
if (typeHandles != null && typeHandles.size() != 0)
{
typesList = new ArrayList<TypeElement>(
typeHandles.size());
for (ElementHandle<TypeElement>
typeHandle : typeHandles)
{
TypeElement element = typeHandle
.resolve(model
.getCompilationController());
if (element != null) {
typesList.add(element);
if ( myDisabledBeans.contains( typeHandle)){
disabled.add( element);
}
}
else {
LOG.warning(typeHandle.toString()
+ " cannot be resolved using: " // NOI18N
+ model.getCompilationController()
.getClasspathInfo());
}
}
}
else {
typesList = Collections.emptyList();
}
return typesList;
}
private void update(final Collection<TypeElement> typeElements,
final Map<Element, TypeElement> productions, final
Set<Element> disabledBeans,
CompilationController controller)
{
if (typeElements.size()==0 && productions.size() == 0 ) {
return;
}
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
Map<Element, InjectableTreeNode<? extends Element>> elementMap=
new LinkedHashMap<Element, InjectableTreeNode<? extends Element>>();
for (TypeElement element : typeElements) {
FileObject fileObject = SourceUtils.getFile(ElementHandle
.create(element), controller.getClasspathInfo());
// Type declaration
TypeTreeNode node = new TypeTreeNode(fileObject,
element, disabledBeans.contains( element),
controller);
insertTreeNode(elementMap, element, node, root,
disabledBeans.contains(element), controller);
}
for (Entry<Element,TypeElement> entry : productions.entrySet()){
Element element = entry.getKey();
FileObject fileObject = SourceUtils.getFile(ElementHandle
.create(element), controller.getClasspathInfo());
if ( element instanceof ExecutableElement ){
// Method definition
MethodTreeNode node = new MethodTreeNode(fileObject,
(ExecutableElement)element,
(DeclaredType)entry.getValue().asType(),
disabledBeans.contains( element), controller);
insertTreeNode( elementMap , (ExecutableElement)element ,
node , root , controller);
}
else {
// Should be produces field.
InjectableTreeNode<Element> node =
new InjectableTreeNode<Element>(fileObject, element,
(DeclaredType)entry.getValue().asType(),
disabledBeans.contains(element),controller);
insertTreeNode( elementMap , node , root );
}
}
setRoot(root);
}
private void insertTreeNode( Map<Element,
InjectableTreeNode<? extends Element>> elementMap,TypeElement element ,
TypeTreeNode node, DefaultMutableTreeNode root , boolean isDisabled,
CompilationController controller)
{
TypeTreeNode parent = null;
for( Entry<Element, InjectableTreeNode<? extends Element>> entry :
elementMap.entrySet())
{
Element key = entry.getKey();
if ( !( key instanceof TypeElement )){
continue;
}
TypeTreeNode injectableNode = (TypeTreeNode)entry.getValue();
TypeElement typeElement = (TypeElement)key;
if ( controller.getTypes().isAssignable( element.asType(),
typeElement.asType()))
{
if ( parent == null ){
parent = injectableNode;
}
else if ( parent.isAssignableFrom(typeElement, controller)){
parent = injectableNode;
}
}
}
DefaultMutableTreeNode parentNode = parent;
if ( parentNode == null ){
parentNode = root;
}
Enumeration<?> children = parentNode.children();
List<TypeTreeNode> movedChildren = new LinkedList<TypeTreeNode>();
while (children.hasMoreElements()) {
TypeTreeNode childNode = (TypeTreeNode) children.nextElement();
if (childNode.isAssignable(element, controller))
{
movedChildren.add(childNode);
}
}
for (TypeTreeNode typeTreeNode : movedChildren) {
parentNode.remove(typeTreeNode);
node.add(typeTreeNode);
}
parentNode.add(node);
elementMap.put(element, node);
}
static void insertTreeNode( Map<Element,
InjectableTreeNode<? extends Element>> elementMap,
ExecutableElement element , MethodTreeNode node,
DefaultMutableTreeNode root , CompilationController controller)
{
MethodTreeNode parent = null;
List<ExecutableElement> overriddenMethods = new ArrayList<ExecutableElement>();
ExecutableElement overriddenMethod = element;
while ( true ){
overriddenMethod =
controller.getElementUtilities().getOverriddenMethod(overriddenMethod);
if ( overriddenMethod == null ){
break;
}
overriddenMethods.add( overriddenMethod );
}
if ( overriddenMethods.size() > 0 )
{
for (Entry<Element, InjectableTreeNode<? extends Element>> entry :
elementMap.entrySet())
{
Element key = entry.getKey();
if (!(key instanceof ExecutableElement)) {
continue;
}
MethodTreeNode injectableNode = (MethodTreeNode) entry
.getValue();
ExecutableElement method = (ExecutableElement) key;
int index = overriddenMethods.indexOf( method);
if ( index != -1 ) {
if (parent == null) {
parent = injectableNode;
}
else if (parent.isOverridden( index, overriddenMethods,
controller))
{
parent = injectableNode;
}
}
}
}
DefaultMutableTreeNode parentNode = parent;
if ( parentNode == null ){
parentNode = root;
}
Enumeration<?> children = parentNode.children();
List<MethodTreeNode> movedChildren = new LinkedList<MethodTreeNode>();
while (children.hasMoreElements()) {
Object child = children.nextElement();
if (child instanceof MethodTreeNode) {
MethodTreeNode childNode = (MethodTreeNode)child;
if (childNode.overridesMethod(element, controller)) {
movedChildren.add(childNode);
}
}
}
for (MethodTreeNode methodNode : movedChildren) {
parentNode.remove(methodNode);
node.add(methodNode);
}
parentNode.add(node);
elementMap.put(element, node);
}
private void insertTreeNode( Map<Element,
InjectableTreeNode<? extends Element>> elementMap,
InjectableTreeNode<Element> node, DefaultMutableTreeNode root )
{
root.add( node );
}
private MetadataModel<WebBeansModel> getModel(){
return myModel;
}
private List<ElementHandle<TypeElement>> myTypeHandles;
private Map<ElementHandle<?>,ElementHandle<TypeElement>> myProductionHandles;
private Set<ElementHandle<?>> myDisabledBeans;
private MetadataModel<WebBeansModel> myModel;
}