blob: 0516701ec74d48932f95b4fa1f13fce09c60b3a0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 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
*******************************************************************************/
package org.eclipse.pde.api.tools.internal.search;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.pde.api.tools.internal.IApiCoreConstants;
import org.eclipse.pde.api.tools.internal.IApiXmlConstants;
import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.builder.IReference;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement;
import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;
import org.eclipse.pde.api.tools.internal.util.Util;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* Processes API problem filter files into a collection of reference filters. The
* filters can later be applied to a set of references to exclude references with
* matching to/from types and members.
*
* @since 1.0.500
*/
public class ReferenceFilterStore {
/**
* Describes a single reference filter. Contents can be accessed via fields.
*/
class ReferenceFilter{
/**
* The type being referenced, possibly <code>null</code>
*/
public String referencedTypeName = null;
/**
* The local type referencing the {@link #referencedTypeName}, possibly <code>null</code>
*/
public String localTypeName = null;
/**
* The field or method from {@link #localTypeName} that is referencing {@link #referencedTypeName}, possibly <code>null</code>
*/
public String fieldOrMethodName = null;
/**
* Constructs a new reference filter with available information.
* @param referencedTypeName
* @param localTypeName
* @param fieldOrMethodName
*/
public ReferenceFilter(String referencedTypeName, String localTypeName, String fieldOrMethodName){
this.referencedTypeName = referencedTypeName;
this.localTypeName = localTypeName;
this.fieldOrMethodName = fieldOrMethodName;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj instanceof ReferenceFilter){
ReferenceFilter filter = (ReferenceFilter)obj;
if ((filter.referencedTypeName == null && referencedTypeName == null) || (filter.referencedTypeName != null && filter.referencedTypeName.equals(referencedTypeName))){
if ((filter.localTypeName == null && localTypeName == null) || (filter.localTypeName != null && filter.localTypeName.equals(localTypeName))){
if ((filter.fieldOrMethodName == null && fieldOrMethodName == null) || (filter.fieldOrMethodName != null && filter.fieldOrMethodName.equals(fieldOrMethodName))){
return true;
}
}
}
}
return false;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
int hashCode = 1;
if (referencedTypeName != null){
hashCode += referencedTypeName.hashCode();
}
if (localTypeName != null){
hashCode += localTypeName.hashCode();
}
if (fieldOrMethodName != null){
hashCode += fieldOrMethodName.hashCode();
}
return hashCode;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return "Reference Filter: " + referencedTypeName + " | " + localTypeName + " | " + fieldOrMethodName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
/**
* Fully qualified type name to a IApiProblem
*/
private Map/*<String, IApiProblem>*/ fFilterMap;
private int fFilterCount;
private boolean debug = false;
/**
* Constructs a new filter store for the given componentID, processing all appropriate
* api filters files found in the directory specified by the filtersRoot string path.
*
* @param filterRoot path to folder containing api problem filters files
* @param componentID string id of the component this filter store is for
* @param debug whether to print out debug statements to the system out stream
*/
public ReferenceFilterStore(String filterRoot, String componentID, boolean debug){
this.debug = debug;
this.initialize(filterRoot, componentID);
}
/**
* Disposes of any resources this filter store is using
*/
public void dispose(){
// Map should be handled by GC, but clear it in case something is holding on to a filter
fFilterMap.clear();
fFilterMap = null;
fFilterCount = 0;
}
/**
* Returns whether to filter the given reference because it matches one of
* the filters in this filter store.
*
* @param reference the reference to compare against filters, the member's type must be available
* @return <code>true</code> if the reference matches a filter
*/
public boolean isFiltered(IReference reference){
IApiElement member = reference.getMember();
while (member.getType() != IApiElement.TYPE){
member = member.getParent();
if (member == null){
// No parent type could be found
return false;
}
}
// member.getName gives qualified name for types
Set filters = (Set)fFilterMap.get(member.getName());
// XXX Remove
System.out.println("Search filters for " + member.getName());
if (filters != null){
System.out.println(filters.size() + " filters available for " + member.getName());
}
if (filters != null){
// Referencing types match, check referenced type names and method/field names
// TODO
}
return false;
}
/**
* Returns the number of filters in this filter store
*
* @return number of filters in this filter store
*/
public int getFilterCount(){
return fFilterCount;
}
/**
* Initialize the filter store using the given component id
*/
private void initialize(String filtersRoot, String componentID) {
if(fFilterMap != null) {
return;
}
fFilterCount = 0;
fFilterMap = new HashMap(5);
String xml = null;
InputStream contents = null;
try {
File filterFileParent = new File(filtersRoot, componentID);
if (!filterFileParent.exists()) {
if(this.debug) {
System.out.println("No filters found for component " + componentID); //$NON-NLS-1$
}
return;
}
contents = new BufferedInputStream(new FileInputStream(new File(filterFileParent, IApiCoreConstants.API_FILTERS_XML_NAME)));
xml = new String(Util.getInputStreamAsCharArray(contents, -1, IApiCoreConstants.UTF_8));
}
catch(IOException ioe) {}
finally {
if (contents != null) {
try {
contents.close();
} catch(IOException e) {
// ignore
}
}
}
if(xml == null) {
return;
}
Element root = null;
try {
root = Util.parseDocument(xml);
}
catch(CoreException ce) {
ApiPlugin.log(ce);
}
if (root == null) {
return;
}
if (!root.getNodeName().equals(IApiXmlConstants.ELEMENT_COMPONENT)) {
return;
}
String component = root.getAttribute(IApiXmlConstants.ATTR_ID);
if(component.length() == 0) {
return;
}
int version = loadIntegerAttribute(root, IApiXmlConstants.ATTR_VERSION);
if (version < 2) {
if(this.debug) {
System.out.println("All filters of versions earlier than 2 are discarded because there is no way to retrieve the type name"); //$NON-NLS-1$
}
// we discard all filters since there is no way to retrieve the type name
return;
}
NodeList resources = root.getElementsByTagName(IApiXmlConstants.ELEMENT_RESOURCE);
for(int i = 0; i < resources.getLength(); i++) {
Element element = (Element) resources.item(i);
String typeName = element.getAttribute(IApiXmlConstants.ATTR_TYPE);
if(typeName == null || typeName.length() == 0) {
// Only problems with types are used to filter references
continue;
}
NodeList filters = element.getElementsByTagName(IApiXmlConstants.ELEMENT_FILTER);
for(int j = 0; j < filters.getLength(); j++) {
element = (Element) filters.item(j);
int id = loadIntegerAttribute(element, IApiXmlConstants.ATTR_ID);
if(id <= 0) {
continue;
}
String[] messageargs = null;
NodeList elements = element.getElementsByTagName(IApiXmlConstants.ELEMENT_PROBLEM_MESSAGE_ARGUMENTS);
if (elements.getLength() != 1) continue;
Element messageArguments = (Element) elements.item(0);
NodeList arguments = messageArguments.getElementsByTagName(IApiXmlConstants.ELEMENT_PROBLEM_MESSAGE_ARGUMENT);
int length = arguments.getLength();
messageargs = new String[length];
for (int k = 0; k < length; k++) {
Element messageArgument = (Element) arguments.item(k);
messageargs[k] = messageArgument.getAttribute(IApiXmlConstants.ATTR_VALUE);
}
ReferenceFilter reference = recoverReference(ApiProblemFactory.newApiProblem(null, typeName, messageargs, null, null, -1, -1, -1, id));
// XXX Remove
System.out.println(reference);
if (reference != null){
Set filterSet = (Set) fFilterMap.get(typeName);
if(filterSet == null) {
filterSet = new HashSet();
fFilterMap.put(typeName, filterSet);
}
filterSet.add(reference);
fFilterCount++;
}
}
}
if (debug){
System.out.println(fFilterCount + " reference filters found for component " + componentID); //$NON-NLS-1$
}
}
private int loadIntegerAttribute(Element element, String name) {
String value = element.getAttribute(name);
if(value.length() == 0) {
return -1;
}
try {
int number = Integer.parseInt(value);
return number;
}
catch(NumberFormatException nfe) {}
return -1;
}
/**
* Returns a reference object created by parsing the message arguments, type and id of the given problem or
* <code>null</code> if the problem does not reflect a reference. The returned reference may be missing information
* and the type/method names may not be fully qualified.
*
* @param problem the problem to recover a reference from
* @return a reference containing all information collected from the problem or <code>null</code> if a reference cannot be recovered
*/
private ReferenceFilter recoverReference(IApiProblem problem) {
if (problem.getCategory() == IApiProblem.CATEGORY_USAGE){
/*
#api usage problems
#{0} = referenced type or member name
#{1} = local type name
#{2} = field or method name
#{3} = required execution environment
*/
String referencedTypeName = null;
String localTypeName = null;
String fieldOrMethodName = null;
String[] arguments = problem.getMessageArguments();
if (arguments.length > 0){
referencedTypeName = arguments[0];
if (arguments.length > 1){
localTypeName = arguments[1];
if (arguments.length > 2){
fieldOrMethodName = arguments[2];
}
}
return new ReferenceFilter(referencedTypeName, localTypeName, fieldOrMethodName);
}
}
return null;
}
}