/*******************************************************************************
* Copyright (c) 2004, 2016 Red Hat, Inc.
* 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:
* Red Hat Incorporated - initial API and implementation
*******************************************************************************/
/*
* Initially created on Jul 8, 2004
*/
/**
* @author Chris Moller, Red Hat, Inc.
* @author Jeff Johnston, Red Hat, Inc. (rewrite to use ICHelpProvider)
* Modified to be org.eclipse.linuxtools.cdt.libhover package.
*/
package org.eclipse.linuxtools.internal.cdt.libhover;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTCompletionNode;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameterMap;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.ICHelpBook;
import org.eclipse.cdt.ui.ICHelpProvider;
import org.eclipse.cdt.ui.ICHelpResourceDescriptor;
import org.eclipse.cdt.ui.IFunctionSummary;
import org.eclipse.cdt.ui.IRequiredInclude;
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
import org.eclipse.cdt.ui.text.IContentAssistHelpInvocationContext;
import org.eclipse.cdt.ui.text.IHoverHelpInvocationContext;
import org.eclipse.cdt.ui.text.SharedASTJob;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.IFileSystem;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.core.runtime.Status;
import org.eclipse.help.IHelpResource;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IRegion;
import org.eclipse.linuxtools.cdt.libhover.ClassInfo;
import org.eclipse.linuxtools.cdt.libhover.FunctionInfo;
import org.eclipse.linuxtools.cdt.libhover.HelpBook;
import org.eclipse.linuxtools.cdt.libhover.LibHoverInfo;
import org.eclipse.linuxtools.cdt.libhover.LibhoverPlugin;
import org.eclipse.linuxtools.cdt.libhover.MemberInfo;
import org.eclipse.linuxtools.internal.cdt.libhover.preferences.PreferenceConstants;
public class LibHover implements ICHelpProvider {
public final static String LIBHOVER_DOC_EXTENSION = LibhoverPlugin.PLUGIN_ID + ".library"; //$NON-NLS-1$
// see comment in initialize()
// private static String defaultSearchPath = null;
private static ConcurrentHashMap<ICHelpBook, LibHoverLibrary> libraries = new ConcurrentHashMap<>();
static final String constructTypes[] = {
"dtype", //$NON-NLS-1$
"enum", //$NON-NLS-1$
"function", //$NON-NLS-1$
"groupsynopsis", //$NON-NLS-1$
"struct", //$NON-NLS-1$
"type", //$NON-NLS-1$
"union" //$NON-NLS-1$
};
static final int dtypeIndex = 0;
static final int enumIndex = 1;
static final int functionIndex = 2;
static final int groupsynopsisIndex = 3;
static final int structIndex = 4;
static final int typeIndex = 5;
static final int unionIndex = 6;
private static ArrayList<ICHelpBook> helpBooks = new ArrayList<>();
private static Map<String, ICHelpBook> helpBooksMap = new HashMap<>();
public static boolean docsFetched = false;
public static Collection<LibHoverLibrary> getLibraries() {
return libraries.values();
}
public static void saveLibraries(IPath locationBase, IPreferenceStore ps) {
// If user preference is to cache libhover data, then save any un-saved
// library hover data.
if (ps.getBoolean(PreferenceConstants.CACHE_EXT_LIBHOVER)) {
for (Iterator<LibHoverLibrary> i = libraries.values().iterator(); i.hasNext();) {
LibHoverLibrary l = i.next();
try {
// Now, output the LibHoverInfo for caching later
IPath locationDir = locationBase;
if (l.isCPP()) {
locationDir = locationBase.append("CPP"); //$NON-NLS-1$
} else {
locationDir = locationBase.append("C"); //$NON-NLS-1$
}
File lDir = new File(locationDir.toOSString());
lDir.mkdir();
IPath location = locationDir.append(getTransformedName(l.getName()) + ".libhover"); //$NON-NLS-1$
File target = new File(location.toOSString());
if (!target.exists()) {
try (FileOutputStream f = new FileOutputStream(locationDir.append("tmpFile").toOSString()); //$NON-NLS-1$
ObjectOutputStream out = new ObjectOutputStream(f)) {
out.writeObject(l.getHoverInfo());
out.close();
File tmp = new File(locationDir.append("tmpFile").toOSString()); //$NON-NLS-1$
tmp.renameTo(target);
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
public static synchronized void getLibHoverDocs() {
if (docsFetched) {
return;
}
libraries.clear();
helpBooks.clear();
helpBooksMap.clear();
// Check if caching of library info is enabled and if so, get any
// cached library hover info.
IPreferenceStore ps = LibhoverPlugin.getDefault().getPreferenceStore();
if (ps.getBoolean(PreferenceConstants.CACHE_EXT_LIBHOVER)) {
// Look for cached libhover files in the plugin state location
IPath stateLocation = LibhoverPlugin.getDefault().getStateLocation();
IFileSystem fs = EFS.getLocalFileSystem();
IPath CLibraryLocation = stateLocation.append("C"); //$NON-NLS-1$
IPath CPPLibraryLocation = stateLocation.append("CPP"); //$NON-NLS-1$
IFileStore cDir = fs.getStore(CLibraryLocation);
if (cDir.fetchInfo().exists()) {
getCachedLibraries(cDir, "C"); //$NON-NLS-1$
}
IFileStore cppDir = fs.getStore(CPPLibraryLocation);
if (cppDir.fetchInfo().exists()) {
getCachedLibraries(cppDir, "C++"); //$NON-NLS-1$
}
}
IExtensionRegistry x = RegistryFactory.getRegistry();
IConfigurationElement[] ces = x.getConfigurationElementsFor(LIBHOVER_DOC_EXTENSION);
for (int i = 0; i < ces.length; ++i) {
IConfigurationElement ce = ces[i];
if (ce.getName().equals("library")) { //$NON-NLS-1$
// see comment in initialize()
// Use the FileLocator class to open the magic hover doc file
// in the plugin's jar.
// Either open the html file or file system file depending
// on what has been specified.
String location = ce.getAttribute("location"); //$NON-NLS-1$
String name = ce.getAttribute("name"); //$NON-NLS-1$
String helpdocs = ce.getAttribute("docs"); //$NON-NLS-1$
String type = ce.getAttribute("type"); //$NON-NLS-1$
String nameSpace = ce.getContributor().getName();
// If library not already cached, create it
ICHelpBook book = helpBooksMap.get(name);
if (book == null) {
HelpBook h = new HelpBook(name, type);
helpBooks.add(h);
helpBooksMap.put(name, h);
LibHoverLibrary l = new LibHoverLibrary(name, location, helpdocs, nameSpace,
"C++".equals(type)); //$NON-NLS-1$
libraries.put(h, l);
} else {
LibHoverLibrary l = libraries.get(book);
if (l != null) {
l.setDocs(helpdocs);
}
}
docsFetched = true;
}
}
}
private static String getTransformedName(String name) {
return name.replaceAll("\\s", "_"); //$NON-NLS-1$ //$NON-NLS-2$
}
private static String getCleanName(String name) {
return name.replaceAll("_", " "); //$NON-NLS-1$ //$NON-NLS-2$
}
private static void getCachedLibraries(IFileStore dir, String type) {
try {
boolean isCPP = type.equals("C++"); //$NON-NLS-1$
IFileStore[] files = dir.childStores(EFS.NONE, null);
for (int i = 0; i < files.length; ++i) {
IFileStore file = files[i];
String fileName = file.fetchInfo().getName();
if (fileName.endsWith(".libhover")) { //$NON-NLS-1$
File f = file.toLocalFile(EFS.NONE, null);
if (f != null) {
String name = getCleanName(fileName.substring(0,fileName.length()-9));
HelpBook h = new HelpBook(name, type);
helpBooks.add(h);
helpBooksMap.put(name, h);
String location = file.toURI().toString();
LibHoverLibrary l = new LibHoverLibrary(name, location, null, null, isCPP);
libraries.put(h, l);
}
}
}
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void initialize() {
getLibHoverDocs();
}
@Override
public ICHelpBook[] getCHelpBooks () {
ICHelpBook[] chelpbooks = new ICHelpBook[helpBooks.size()];
return helpBooks.toArray(chelpbooks);
}
private static class FunctionSummary implements IFunctionSummary, Comparable<FunctionSummary> {
private String Name;
private String NameSpace;
private String ReturnType;
private String Prototype;
private String Summary;
private boolean prototypeHasBrackets;
private class RequiredInclude implements IRequiredInclude {
private final String include;
public RequiredInclude (String file) {
include = file;
}
@Override
public String getIncludeName() {
return include;
}
@Override
public boolean isStandard() {
return true;
}
}
@Override
public int compareTo (FunctionSummary x) {
FunctionSummary y = x;
return getName().compareTo(y.getName());
}
private final ArrayList<RequiredInclude> Includes = new ArrayList<>();
private void setIncludeName (String iname) {
RequiredInclude nri = new RequiredInclude(iname);
Includes.add(nri);
}
public class FunctionPrototypeSummary implements IFunctionPrototypeSummary {
@Override
public String getName() { return Name; }
@Override
public String getReturnType() { return ReturnType; }
@Override
public String getArguments() { return Prototype; }
@Override
public String getPrototypeString(boolean namefirst) {
if (namefirst) {
if (prototypeHasBrackets()) {
return Name + " " + Prototype + " " + ReturnType; //$NON-NLS-1$ //$NON-NLS-2$
}
return Name + " (" + Prototype + ") " + ReturnType; //$NON-NLS-1$ //$NON-NLS-2$
} else {
if (prototypeHasBrackets()) {
return ReturnType + " " + Name + " " + Prototype; //$NON-NLS-1$ //$NON-NLS-2$
}
return ReturnType + " " + Name + " (" + Prototype + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
@Override
public String getName() { return Name; }
@Override
public String getNamespace() { return NameSpace; }
@Override
public String getDescription() { return Summary; }
public boolean prototypeHasBrackets() { return prototypeHasBrackets; }
public void setPrototypeHasBrackets(boolean value) { prototypeHasBrackets = value; }
@Override
public IFunctionPrototypeSummary getPrototype() { return new FunctionPrototypeSummary(); }
@Override
public IRequiredInclude[] getIncludes() {
IRequiredInclude[] includes = new IRequiredInclude[Includes.size()];
for (int i = 0; i < Includes.size(); ++i) {
includes[i] = Includes.get(i);
}
return includes;
}
}
private static class EnclosingASTNameJob extends SharedASTJob {
private final int tlength;
private final int toffset;
private IASTName result = null;
public EnclosingASTNameJob (ITranslationUnit t,
int toffset, int tlength) {
super("EnclosingASTNameJob", t); //$NON-NLS-1$
this.toffset = toffset;
this.tlength = tlength;
}
@Override
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) {
if (ast != null) {
result = ast.getNodeSelector(null).findEnclosingName(toffset, tlength);
}
return Status.OK_STATUS;
}
public IASTName getASTName() {
return result;
}
}
@Override
public IFunctionSummary getFunctionInfo(ICHelpInvocationContext context, ICHelpBook[] helpBooks, String name) {
IFunctionSummary f;
f = null;
ITranslationUnit t = context.getTranslationUnit();
String className = null;
ICPPFunctionType methodType = null;
if (t.isCXXLanguage()) {
try {
if (context instanceof IHoverHelpInvocationContext) {
// We know the file offset of the member reference.
IRegion region = ((IHoverHelpInvocationContext)context).getHoverRegion();
// Now, let's find the declaration of the method. We need to do this because we want the specific
// member prototype to go searching for. There could be many members called "x" which have different
// documentation.
final IASTName[] result= {null};
EnclosingASTNameJob job = new EnclosingASTNameJob(t, region.getOffset(), region.getLength());
job.schedule();
try {
job.join();
} catch (InterruptedException e) {
// Bug: 470309
return null; // just return
}
if (job.getResult() == Status.OK_STATUS) {
result[0] = job.getASTName();
}
if (result[0] != null) {
final IBinding binding = result[0].getBinding();
// Check to see we have a member function.
if (binding instanceof ICPPFunction) {
methodType = ((ICPPFunction)binding).getType();
// We have a member function, find the class name.
IBinding owner = ((ICPPFunction)binding).getOwner();
if (owner instanceof ICPPClassType) {
className = getClassName((ICPPClassType)owner);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// Loop through all the documents we have and report first match.
for (int i = 0; i < helpBooks.length; ++i) {
LibHoverLibrary l = libraries.get(helpBooks[i]);
if (name != null) {
if (className != null) {
if (l.isCPP()) {
f = getMemberSummary(l, className, name, methodType);
}
} else {
f = getFunctionSummary(l, name);
}
if (f != null) {
return f;
}
}
}
return null;
}
// Get the class name for a type, including any instance template parameters
// e.g. std::basic_string<char>
private String getClassName(ICPPClassType c) {
String className = null;
try {
String[] qualified = c.getQualifiedName();
className = qualified[0];
for (int k = 1; k < qualified.length; ++k) {
className += "::" + qualified[k]; //$NON-NLS-1$
}
// Check if we have an instance of a template class.
if (c instanceof ICPPTemplateInstance) {
ICPPTemplateInstance ti = (ICPPTemplateInstance)c;
// Get a map which tells us the values of the template
// arguments (e.g. _CharT maps to char in the instance).
ICPPTemplateParameterMap tiMap = ti.getTemplateParameterMap();
ICPPTemplateDefinition td = ti.getTemplateDefinition();
ICPPTemplateParameter[] templateArgs = td.getTemplateParameters();
className += "<"; //$NON-NLS-1$
String separator = ""; //$NON-NLS-1$
for (int x = 0; x < templateArgs.length; ++x) {
ICPPTemplateParameter tp = templateArgs[x];
ICPPTemplateArgument ta = tiMap.getArgument(tp);
IType type = null;
// The template may have a type specified or a value.
// In the case of a value, figure out its type and use
// that when we do a lookup.
if (ta.isTypeValue()) {
type = ta.getTypeValue();
} else {
type = ta.getTypeOfNonTypeValue();
}
if (tp.getTemplateNestingLevel() == 0) {
// if the parameter is a class type, use recursion to
// get its class name including template parameters
if (type instanceof ICPPClassType) {
className += separator + getClassName((ICPPClassType)type);
} else {
className += separator + type.toString();
}
separator = ","; //$NON-NLS-1$
}
}
className += ">"; //$NON-NLS-1$
}
} catch(DOMException e) {
return null;
}
return className;
}
private IFunctionSummary getFunctionSummary(LibHoverLibrary l, String name) {
FunctionInfo x = l.getFunctionInfo(name);
if (x != null) {
FunctionSummary f = new FunctionSummary();
f.ReturnType = x.getReturnType();
f.Prototype = x.getPrototype();
f.Summary = x.getDescription();
f.Name = x.getName();
ArrayList<String> headers = x.getHeaders();
for (int i = 0; i < headers.size(); ++i) {
f.setIncludeName(headers.get(i));
}
return f;
}
return null;
}
private IFunctionSummary getMemberSummary(LibHoverLibrary l, String className,
String memberName, ICPPFunctionType methodType) {
ArrayList<String> templateTypes = new ArrayList<>();
ClassInfo info = l.getClassInfo(className, templateTypes);
String[] args = new String[0];
@SuppressWarnings("unused")
IType returnType = null;
if (info == null) {
return null;
}
if (methodType != null) {
try {
args = resolveArgs(info, methodType.getParameterTypes(), templateTypes);
returnType = methodType.getReturnType();
} catch (Exception e) {
return null;
}
}
MemberInfo member = info.getMember(memberName);
if (member != null) {
MemberInfo m = null;
if (!isParmMatch(member, args, templateTypes, info)) {
ArrayList<MemberInfo> members = member.getChildren();
for (int i = 0; i < members.size(); ++i) {
MemberInfo k = members.get(i);
if (isParmMatch(k, args, templateTypes, info)) {
m = k;
break;
}
}
} else {
m = member;
}
if (m != null) {
// FIXME: do some work to determine parameters and return type.
FunctionSummary f = new FunctionSummary();
f.ReturnType = m.getReturnType();
f.Prototype = m.getPrototype();
f.Summary = m.getDescription();
f.Name = className + "::" + memberName; //$NON-NLS-1$
String[] templateParms = info.getTemplateParms();
for (int i = 0; i < templateTypes.size(); ++i) {
f.ReturnType = f.ReturnType.replaceAll(templateParms[i], templateTypes.get(i));
f.Prototype = f.Prototype.replaceAll(templateParms[i], templateTypes.get(i));
f.Name = f.Name.replaceAll(templateParms[i], templateTypes.get(i));
}
if (f.ReturnType.indexOf('<') >= 0) {
f.ReturnType = f.ReturnType.replaceAll("<", "<"); //$NON-NLS-1$ //$NON-NLS-2$
f.ReturnType = f.ReturnType.replaceAll(">", ">"); //$NON-NLS-1$//$NON-NLS-2$
}
if (f.Prototype.indexOf('<') >= 0) {
f.Prototype = f.Prototype.replaceAll("<", "<"); //$NON-NLS-1$ //$NON-NLS-2$
f.Prototype = f.Prototype.replaceAll(">", ">"); //$NON-NLS-1$//$NON-NLS-2$
}
if (f.Name.indexOf('<') >= 0) {
f.Name = f.Name.replaceAll("<", "<"); //$NON-NLS-1$//$NON-NLS-2$
f.Name = f.Name.replaceAll(">", ">"); //$NON-NLS-1$ //$NON-NLS-2$
}
f.setPrototypeHasBrackets(true);
f.setIncludeName(info.getInclude());
return f;
}
}
return null;
}
private boolean isParmMatch(MemberInfo m, String[] args, ArrayList<String> templateTypes, ClassInfo info) {
String[] memberParms = m.getParamTypes();
String className = info.getClassName();
int index = className.lastIndexOf("::"); //$NON-NLS-1$
String unqualifiedName = className.substring(index+2);
for (int i = 0; i < memberParms.length; ++i) {
String[] templateParms = info.getTemplateParms();
for (int j = 0; j < templateTypes.size(); ++j) {
memberParms[i] = memberParms[i].replaceAll(templateParms[j], templateTypes.get(j));
}
// Look for the class being passed by reference...the doc prototype will not fill in
// the template parms nor the qualifier so we do it here to make sure we match what
// is coming back from the indexer which will be fully-qualified and have template
// parameters specified.
if (memberParms[i].contains(unqualifiedName) && !memberParms[i].contains(className)) {
String classTemplate = ""; //$NON-NLS-1$
if (templateTypes.size() > 0) {
classTemplate = "<"; //$NON-NLS-1$
String separator = ""; //$NON-NLS-1$
for (int j = 0; j < templateTypes.size(); ++j) {
classTemplate += separator + templateTypes.get(j);
separator = ","; //$NON-NLS-1$
}
classTemplate += ">"; //$NON-NLS-1$
}
memberParms[i] = memberParms[i].replaceAll(unqualifiedName, className + classTemplate);
}
}
return Arrays.equals(memberParms, args);
}
private String[] resolveArgs(ClassInfo info, IType[] parameterTypes, ArrayList<String> templateTypes) {
String[] templateParms = info.getTemplateParms();
String[] result = new String[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; ++i) {
String param = parameterTypes[i].toString();
param = param.replaceAll("\\{.*\\}", ""); //$NON-NLS-1$ //$NON-NLS-2$
param = param.trim();
int index = param.indexOf('#');
while (index >= 0) {
// We assume no class has more than 9 template parms.
int digit = param.charAt(index + 1) - '0';
// where possible, replace template parms with real values
if (digit < templateTypes.size()) {
param = param.replaceFirst(param.substring(index, index + 2), templateTypes.get(digit));
} else {
param = param.replaceFirst(param.substring(index, index + 2), templateParms[digit]);
}
index = param.indexOf('#');
}
result[i] = param;
}
return result;
}
@Override
public IFunctionSummary[] getMatchingFunctions(ICHelpInvocationContext context, ICHelpBook[] helpBooks, String prefix) {
ArrayList<IFunctionSummary> fList = new ArrayList<>();
ITranslationUnit t = context.getTranslationUnit();
boolean qualifiedCPP = false;
if (t.isCXXLanguage()) {
try {
if (context instanceof IContentAssistHelpInvocationContext) {
// We know the file offset of the member reference.
IASTCompletionNode node = ((IContentAssistHelpInvocationContext)context).getCompletionNode();
IASTName[] names = node.getNames();
for (IASTName name : names) {
if (name.isQualified()) {
qualifiedCPP = true;
break;
}
}
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (!qualifiedCPP) {
for (int di = 0; di < helpBooks.length; ++di) {
LibHoverLibrary l = libraries.get(helpBooks[di]);
LibHoverInfo cppInfo = l.getHoverInfo();
SortedMap<String, FunctionInfo> map = cppInfo.functions.tailMap(prefix);
Set<Map.Entry<String, FunctionInfo>> c = map.entrySet();
for (Iterator<Entry<String, FunctionInfo>> i = c.iterator(); i.hasNext();) {
Map.Entry<String, FunctionInfo> e = i.next();
FunctionInfo x = e.getValue();
String name = x.getName();
// Look for names that start with prefix, but ignore names that
// start with "0" which is used to import text data that cannot
// be omitted from the binary version of the document (e.g. invariant
// sections of a GFDL licensed document). This data is given a
// function name that starts with the character "0" which is not
// valid for the start of a C/C++ function name. As such, it should
// never be offered as a choice for an empty prefix.
if (name.startsWith(prefix) && !name.startsWith("0")) { //$NON-NLS-1$
FunctionSummary f = new FunctionSummary();
f.ReturnType = x.getReturnType();
f.Prototype = x.getPrototype();
f.Summary = x.getDescription();
f.Name = x.getName();
ArrayList<String> headers = x.getHeaders();
for (int i1 = 0; i1 < headers.size(); ++i1) {
f.setIncludeName(headers.get(i1));
}
fList.add(f);
}
}
}
}
IFunctionSummary[] summaries = new IFunctionSummary[fList.size()];
for (int k = 0; k < summaries.length; k++) {
summaries[k] = fList.get(k);
}
return summaries;
}
private static class HelpResource implements IHelpResource {
private final String href;
private final String label;
public HelpResource(String href, String label) {
this.href = href;
this.label = label;
}
@Override
public String getHref() {
return href;
}
@Override
public String getLabel() {
return label;
}
}
private static class HelpResourceDescriptor implements ICHelpResourceDescriptor {
private final ICHelpBook helpbook;
public HelpResourceDescriptor(ICHelpBook helpbook) {
this.helpbook = helpbook;
}
@Override
public ICHelpBook getCHelpBook() {
return helpbook;
}
@Override
public IHelpResource[] getHelpResources() {
LibHoverLibrary l = libraries.get(helpbook);
if (l != null) {
IHelpResource[] hr = new IHelpResource[1];
hr[0] = new HelpResource(l.getDocs(), l.getName());
return hr;
}
return null;
}
}
@Override
public ICHelpResourceDescriptor[] getHelpResources(ICHelpInvocationContext context, ICHelpBook[] helpBooks, String name) {
for (int i = 0; i < helpBooks.length; ++i) {
IFunctionSummary fs = getFunctionInfo(context, new ICHelpBook[]{helpBooks[i]}, name);
if (fs != null) {
LibHoverLibrary l = libraries.get(helpBooks[i]);
if (l != null && l.getDocs() != null) {
return new HelpResourceDescriptor[]{new HelpResourceDescriptor(helpBooks[i])};
}
}
}
return null;
}
}