/*******************************************************************************
* Copyright (c) 2001, 2015 IBM Corporation 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
*
* Contributors:
* IBM Corporation - initial API and implementation
* Obeo - Contribution to the EEF project
*******************************************************************************/
package org.eclipse.eef.properties.ui.internal.page;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.eef.properties.ui.api.IEEFSectionDescriptor;
import org.eclipse.eef.properties.ui.api.IEEFTypeMapper;
import org.eclipse.jface.viewers.IFilter;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
/**
* Provides a section filtering mechanism where the selection is an IStructuredSelection and filtering is based on
* class.
*
* @author Anthony Hunter
* @author Stephane Begaudeau
*/
public class EEFTabbedPropertyRegistryClassSectionFilter {
/**
* The type mapper.
*/
private IEEFTypeMapper typeMapper;
/**
* The constructor.
*
* @param typeMapper
* The type mapper
*/
public EEFTabbedPropertyRegistryClassSectionFilter(IEEFTypeMapper typeMapper) {
this.typeMapper = typeMapper;
}
/**
* Verifies if the property section extension represented by sectionElement applies to the given input.
*
* @param descriptor
* the section descriptor.
* @param selection
* the selection.
* @return <code>true</code> if this section applies to the current selection.
*/
@SuppressWarnings({ "checkstyle:returncount" })
public boolean appliesToSelection(IEEFSectionDescriptor descriptor, ISelection selection) {
if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
if (descriptor.getEnablesFor() != IEEFSectionDescriptor.ENABLES_FOR_ANY && structuredSelection.size() != descriptor.getEnablesFor()) {
/**
* enablesFor does not match the size of the selection, do not display section.
*/
return false;
}
Object[] objects = structuredSelection.toArray();
IFilter filter = descriptor.getFilter();
if (filter != null) {
for (Object object : objects) {
if (!filter.select(object)) {
/**
* filter fails so section does not apply to the selection, do not display section.
*/
return false;
}
}
/**
* filter passes for all objects in the selection.
*/
return true;
}
Set<Class<?>> effectiveTypes = new HashSet<Class<?>>();
for (Object object : objects) {
Class<?> remapType = object.getClass();
if (typeMapper != null) {
remapType = typeMapper.mapType(object);
}
if (effectiveTypes.add(remapType)) {
// the effective types of the selection
if (!this.appliesToEffectiveType(descriptor, remapType)) {
return false;
}
}
}
} else {
/* Bug 245690 selection is not a IStructuredSelection */
if (descriptor.getFilter() != null) {
return descriptor.getFilter().select(selection);
}
}
return true;
}
/**
* Indicates if the descriptor can be used with the given input class.
*
* @param descriptor
* The descriptor
* @param inputClass
* The input class
* @return <code>true</code> if it can be used, <code>false</code> otherwise
*/
private boolean appliesToEffectiveType(IEEFSectionDescriptor descriptor, Class<?> inputClass) {
List<String> classTypes = this.getClassTypes(inputClass);
List<String> sectionInputTypes = descriptor.getInputTypes();
for (String type : sectionInputTypes) {
if (classTypes.contains(type)) {
// found a match
return true;
}
}
return false;
}
/**
* Returns the classes and interfaces the given target class extends/implements.
*
* @param target
* The target
*
* @return The name of classes and interfaces implemented and extended by the given class.
*/
protected List<String> getClassTypes(Class<?> target) {
List<String> result = new ArrayList<String>();
// add classes
List<Class<?>> classes = this.computeClassOrder(target);
for (Class<?> aClass : classes) {
result.add(aClass.getName());
}
// add interfaces
result.addAll(this.computeInterfaceNameOrder(classes));
return result;
}
/**
* Returns the list of all the classes extended by the given class.
*
* @param target
* The class
* @return The list of all the classes extended by the given class
*/
private List<Class<?>> computeClassOrder(Class<?> target) {
List<Class<?>> result = new ArrayList<Class<?>>();
Class<?> clazz = target;
while (clazz != null) {
result.add(clazz);
clazz = clazz.getSuperclass();
}
return result;
}
/**
* Returns the list of the name of all the interfaces implemented by the given classes.
*
* @param classes
* The classes
* @return The list of the name of all the interfaces implemented by the given classes
*/
private List<String> computeInterfaceNameOrder(List<Class<?>> classes) {
List<String> result = new ArrayList<String>();
Map<Class<?>, Class<?>> seen = new HashMap<Class<?>, Class<?>>();
for (Class<?> aClass : classes) {
this.internalComputeInterfaceOrder(aClass.getInterfaces(), result, seen);
}
return result;
}
/**
* Computes the order of the interfaces seen.
*
* @param interfaces
* The interfaces
* @param result
* The name of the interfaces
* @param seen
* The map of the interfaces already seen
*/
private void internalComputeInterfaceOrder(Class<?>[] interfaces, List<String> result, Map<Class<?>, Class<?>> seen) {
List<Class<?>> newInterfaces = new ArrayList<Class<?>>(seen.size());
for (int i = 0; i < interfaces.length; i++) {
Class<?> interfac = interfaces[i];
if (seen.get(interfac) == null) {
result.add(interfac.getName());
seen.put(interfac, interfac);
newInterfaces.add(interfac);
}
}
for (Class<?> newInterface : newInterfaces) {
this.internalComputeInterfaceOrder(newInterface.getInterfaces(), result, seen);
}
}
}