blob: 3ad1ef45a00373dd0a14dd7bc037ac96015aa345 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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
*
*******************************************************************************/
package org.eclipse.dltk.ui.documentation;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IMember;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.utils.AdaptUtils;
import org.eclipse.dltk.utils.NatureExtensionManager;
import org.eclipse.jface.resource.ImageDescriptor;
/**
* Helper needed to get access to script documentation.
*
* <p>
* This class is not intended to be subclassed or instantiated by clients.
* </p>
*/
public class ScriptDocumentationAccess {
private static final String DOCUMENTATION_PROVIDERS_EXTENSION_POINT = "org.eclipse.dltk.ui.scriptDocumentationProviders"; //$NON-NLS-1$
private static final NatureExtensionManager<IScriptDocumentationProvider> providers = new NatureExtensionManager<IScriptDocumentationProvider>(
DOCUMENTATION_PROVIDERS_EXTENSION_POINT, IScriptDocumentationProvider.class) {
@Override
protected void initializeDescriptors(List<Object> descriptors) {
Collections.sort(descriptors, new Comparator<Object>() {
int priority(IConfigurationElement element) {
try {
return Integer.parseInt(element.getAttribute("priority"));
} catch (NumberFormatException e) {
return 0;
}
}
@Override
public int compare(Object o1, Object o2) {
return priority((IConfigurationElement) o2) - priority((IConfigurationElement) o1);
}
});
}
@Override
protected IScriptDocumentationProvider[] createEmptyResult() {
return new IScriptDocumentationProvider[0];
}
};
private ScriptDocumentationAccess() {
// do not instantiate
}
private static IScriptDocumentationProvider[] getProviders(String nature) {
return providers.getInstances(nature);
}
private static interface Operation {
Reader getInfo(IScriptDocumentationProvider provider);
}
private static interface Operation2 {
IDocumentationResponse getInfo(IScriptDocumentationProvider provider);
}
private static final int BUFF_SIZE = 2048;
private static Reader merge(String nature, Operation operation) {
StringBuilder buffer = new StringBuilder();
char[] buff = null;
for (IScriptDocumentationProvider p : getProviders(nature)) {
Reader reader = operation.getInfo(p);
if (reader != null) {
if (buffer.length() != 0) {
buffer.append("<hr/>"); //$NON-NLS-1$
}
if (buff == null) {
buff = new char[BUFF_SIZE];
}
try {
int len;
while ((len = reader.read(buff, 0, BUFF_SIZE)) != -1) {
buffer.append(buff, 0, len);
}
} catch (IOException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
}
if (buffer.length() > 0) {
char[] cnt = new char[buffer.length()];
buffer.getChars(0, buffer.length(), cnt, 0);
return new CharArrayReader(cnt);
}
return null;
}
private static IDocumentationResponse merge(String nature, Operation2 operation) {
for (IScriptDocumentationProvider p : getProviders(nature)) {
final IDocumentationResponse response = operation.getInfo(p);
if (response != null) {
return response;
}
}
return null;
}
/**
* Gets a reader for an IMember documentation. Content are found using
* documentation documentationProviders, contributed via extension point. The
* content does contain HTML code describing member. It may be for ex. header
* comment or a man page. (if <code>allowExternal</code> is <code>true</code>)
*
* @param member The member to get documentation for.
* @param allowInherited For procedures and methods: if member doesn't have it's
* own documentation, look into parent types methods.
* @param allowExternal Allows external documentation like man-pages.
* @return Reader for a content, or <code>null</code> if no documentation is
* found.
* @throws ModelException is thrown when the elements documentation can not be
* accessed
* @since 3.0
*/
public static Reader getHTMLContentReader(String nature, final Object member, final boolean allowInherited,
final boolean allowExternal) {
return merge(nature, (Operation) provider -> {
if (provider instanceof IScriptDocumentationProviderExtension2) {
final IScriptDocumentationProviderExtension2 ext = (IScriptDocumentationProviderExtension2) provider;
final IDocumentationResponse response = ext.getDocumentationFor(member);
return DocumentationUtils.getReader(response);
} else if (member instanceof IMember) {
return provider.getInfo((IMember) member, allowInherited, allowExternal);
} else {
return null;
}
});
}
/**
* @since 3.0
*/
public static IDocumentationResponse getDocumentation(String nature, final Object member,
final IAdaptable context) {
return merge(nature, (Operation2) provider -> {
if (provider instanceof IScriptDocumentationProviderExtension2) {
final IScriptDocumentationProviderExtension2 ext = (IScriptDocumentationProviderExtension2) provider;
final IDocumentationResponse response = ext.getDocumentationFor(member);
if (response != null && response.getTitle() == null) {
final IScriptDocumentationTitleAdapter titleAdapter = AdaptUtils.getAdapter(context,
IScriptDocumentationTitleAdapter.class);
if (titleAdapter != null) {
final String title = titleAdapter.getTitle(member);
// TODO (alex) image
if (title != null && title.length() != 0) {
return new DocumentationResponseDelegate(response) {
@Override
public String getTitle() {
return title;
}
private boolean imageEvaluated;
private ImageDescriptor image;
@Override
public ImageDescriptor getImage() {
final ImageDescriptor result = super.getImage();
if (result != null) {
return result;
}
if (!imageEvaluated) {
image = titleAdapter.getImage(member);
imageEvaluated = true;
}
return image;
}
};
}
}
}
return response;
} else if (member instanceof IMember) {
final IMember m = (IMember) member;
return DocumentationUtils.wrap(member, context, provider.getInfo(m, true, true));
} else {
return null;
}
});
}
/**
* Gets a reader for an keyword documentation. Content are found using ALL
* documentation documentationProviders, contributed via extension point. The
* content does contain HTML code describing member.
*
* @param content The keyword to find.
* @return Reader for a content, or <code>null</code> if no documentation is
* found.
* @throws ModelException is thrown when the elements documentation can not be
* accessed
*/
@Deprecated
public static Reader getHTMLContentReader(String nature, final String content) throws ModelException {
return merge(nature, (Operation) provider -> provider.getInfo(content));
}
/**
* Returns the documentation for the specified keyword
*
* @param nature
* @param context
* @param keyword
* @since 2.0
*/
public static Reader getKeywordDocumentation(String nature, final IModelElement context, final String keyword)
throws ModelException {
return merge(nature, (Operation) provider -> {
if (provider instanceof IScriptDocumentationProviderExtension) {
final IScriptDocumentationProviderExtension ext = (IScriptDocumentationProviderExtension) provider;
final IDocumentationResponse response = ext.describeKeyword(keyword, context);
return DocumentationUtils.getReader(response);
}
return provider.getInfo(keyword);
});
}
}