blob: 0d29f3628ebbc6b0a8512ad5a87b7b0f1c46c796 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rhelp.core.http;
import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullElse;
import static org.eclipse.statet.internal.rhelp.core.RHelpWebapp.CAT_DOC;
import static org.eclipse.statet.internal.rhelp.core.RHelpWebapp.CAT_LIBRARY;
import static org.eclipse.statet.internal.rhelp.core.RHelpWebapp.LIBRARY_HELP;
import static org.eclipse.statet.internal.rhelp.core.RHelpWebapp.LIBRARY_HTML;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.internal.rhelp.core.RHelpWebapp;
import org.eclipse.statet.internal.rhelp.core.RHelpWebapp.RequestInfo;
import org.eclipse.statet.internal.rhelp.core.server.ServerClientSupport;
import org.eclipse.statet.rhelp.core.REnvHelp;
import org.eclipse.statet.rhelp.core.REnvHelpConfiguration;
import org.eclipse.statet.rhelp.core.RHelpManager;
import org.eclipse.statet.rhelp.core.RHelpPage;
import org.eclipse.statet.rhelp.core.RHelpTopicLookup;
import org.eclipse.statet.rhelp.core.RPkgHelp;
import org.eclipse.statet.rj.renv.core.REnv;
import org.eclipse.statet.rj.renv.core.REnvConfiguration;
@NonNullByDefault
public abstract class RHelpHttpService {
public static final String HTTP_SCHEME= "http"; //$NON-NLS-1$
public static final String PORTABLE_URI_SCHEME= "erhelp"; //$NON-NLS-1$
/**
* Searches topic in library
*/
private static final String RHELP_TOPIC_PATH= "/topic"; //$NON-NLS-1$
/**
* Shows page (package, package/name or package/topic)
*/
private static final String RHELP_PAGE_PATH= "/page"; //$NON-NLS-1$
private static final String ABOUT_BLANK_URI_STRING= "about:blank"; //$NON-NLS-1$
private static final URI ABOUT_BLANK_URI= URI.create(ABOUT_BLANK_URI_STRING);
private final String contextPath= RHelpWebapp.CONTEXT_PATH;
private final RHelpManager rHelpManager;
public static final String BROWSE_TARGET= "browse"; //$NON-NLS-1$
public RHelpHttpService(final RHelpManager rHelpManager) {
this.rHelpManager= rHelpManager;
}
public abstract boolean ensureIsRunning();
protected abstract String getHost();
protected abstract int getPort();
protected void checkRunning() {
if (!ensureIsRunning()) {
throw new UnsupportedOperationException("Help is not available.");
}
}
public boolean isDynamicUrl(final URI url) {
checkRunning();
final String path;
return (HTTP_SCHEME.equals(url.getScheme())
&& getHost().equals(url.getHost()) && getPort() == url.getPort()
&& (path= url.getPath()) != null && path.startsWith(this.contextPath) );
}
private String getDynamicPath(final URI url) {
return url.getPath().substring(this.contextPath.length());
}
protected URI createHttpUrl(final StringBuilder encPath,
final @Nullable String encQuery, final @Nullable String encFragment)
throws URISyntaxException {
final String host= getHost();
final StringBuilder sb= new StringBuilder(host.length() + encPath.length() + 16);
sb.append(HTTP_SCHEME);
sb.append("://"); //$NON-NLS-1$
sb.append(getHost());
if (getPort() != -1) {
sb.append(':');
sb.append(getPort());
}
sb.append(encPath);
if (encQuery != null) {
sb.append('?');
sb.append(encQuery);
}
if (encFragment != null) {
sb.append('#');
sb.append(encFragment);
}
return new URI(sb.toString());
}
protected URI createHttpUrl(final StringBuilder encPath) {
try {
return createHttpUrl(encPath, null, null);
}
catch (final URISyntaxException e) {
throw new IllegalStateException(e);
}
}
public URI getPageHttpUrl(final RHelpPage page, final String target) {
final RPkgHelp pkgHelp= page.getPackage();
return getPageHttpUrl(pkgHelp.getName(), page.getName(), pkgHelp.getREnv(), target);
}
public URI getPageHttpUrl(final String pkgName, final @Nullable String pageName,
final REnv rEnv, final String target) {
checkRunning();
final StringBuilder p= new StringBuilder(64);
p.append(this.contextPath);
p.append('/');
p.append(target);
p.append('/');
p.append(rEnv.getId());
p.append('/');
p.append(CAT_LIBRARY);
p.append('/');
p.append(pkgName);
p.append('/');
if (pageName != null) {
p.append(LIBRARY_HTML);
p.append('/');
p.append(pageName);
p.append(".html"); //$NON-NLS-1$
}
return createHttpUrl(p);
}
public URI getTopicHttpUrl(final String topic, final @Nullable String pkgName,
final REnv rEnv, final String target) {
checkRunning();
final StringBuilder p= new StringBuilder(64);
p.append(this.contextPath);
p.append('/');
p.append(target);
p.append('/');
p.append(rEnv.getId());
p.append('/');
p.append(CAT_LIBRARY);
p.append('/');
p.append((pkgName != null) ? pkgName : "-");
p.append('/');
p.append(LIBRARY_HELP);
p.append('/');
p.append(topic);
return createHttpUrl(p);
}
public URI getREnvHttpUrl(final REnv rEnv, final String target) {
checkRunning();
final StringBuilder p= new StringBuilder(64);
p.append(this.contextPath);
p.append('/');
p.append(target);
p.append('/');
p.append(rEnv.getId());
p.append('/');
return createHttpUrl(p);
}
public URI getPackageHttpUrl(final RPkgHelp pkgHelp, final String target) {
return getPageHttpUrl(pkgHelp.getName(), null, pkgHelp.getREnv(), target);
}
public @Nullable URI toHttpUrl(final String url,
final @Nullable REnv rEnv, final @Nullable String target) {
checkRunning();
if (url.startsWith("rhelp:///")) { //$NON-NLS-1$
if (rEnv == null || target == null) {
return null;
}
final String path= url.substring(8);
final int idx1= (path.length() > 0) ? path.indexOf('/', 1) : -1;
if (idx1 > 0) {
final String command= path.substring(0, idx1);
if (command.equals(RHELP_PAGE_PATH)) {
final int idx2= path.indexOf('/', idx1 + 1);
if (idx2 > idx1 + 1 && idx2 < path.length() - 1) {
return getPageHttpUrl(path.substring(idx1 + 1, idx2),
path.substring(idx2 + 1), rEnv, target );
}
else {
return getPageHttpUrl(path.substring(idx1 + 1, (idx2 > 0) ? idx2 : path.length()),
null, rEnv, target );
}
}
else if (command.equals(RHELP_TOPIC_PATH)) {
return getTopicHttpUrl(path.substring(idx1 + 1), null, rEnv, target);
}
}
else if (path.length() == 1) { // start
return getREnvHttpUrl(rEnv, target);
}
return null;
}
if (url.startsWith("http://")) { //$NON-NLS-1$
try {
final URI uri= new URI(url);
if (isDynamicUrl(uri)) {
if (rEnv == null && target == null) {
return null;
}
final String path= getDynamicPath(uri);
final int targetEnd= path.indexOf('/', 1);
if (targetEnd > 1) {
final StringBuilder p= new StringBuilder(path.length() + 16);
p.append(this.contextPath);
p.append('/');
p.append((target != null) ? target : path.substring(1, targetEnd));
final String info= path.substring(targetEnd + 1);
if (rEnv != null) {
final int idx3= info.indexOf('/');
if (idx3 < 0) {
return null;
}
p.append('/');
p.append(rEnv.getId());
p.append(info.substring(idx3));
}
else {
p.append('/');
p.append(info);
}
return createHttpUrl(p, uri.getRawQuery(), uri.getRawFragment());
}
return null;
}
return uri;
}
catch (final Exception e) {}
}
return null;
}
public @Nullable URI toHttpUrl(final Object object, final String target) {
if (object == this) {
return ABOUT_BLANK_URI;
}
if (object instanceof REnv) {
return getREnvHttpUrl((REnv) object, target);
}
if (object instanceof REnvHelpConfiguration) {
return getREnvHttpUrl(((REnvHelpConfiguration) object).getREnv(), target);
}
if (object instanceof RPkgHelp) {
return getPackageHttpUrl((RPkgHelp) object, target);
}
if (object instanceof RHelpPage) {
return getPageHttpUrl((RHelpPage) object, target);
}
if (object instanceof RHelpTopicLookup) {
final RHelpTopicLookup lookup= (RHelpTopicLookup) object;
return getTopicHttpUrl(lookup.getTopic(), null, lookup.getREnv(), target);
}
if (object instanceof String) {
final String s= (String) object;
if (s.startsWith("http://")) { //$NON-NLS-1$
try {
return new URI(s);
}
catch (final URISyntaxException e) {}
}
}
return null;
}
public @Nullable Object getContentOfUrl(final String url) {
try {
return getContentOfUrl(new URI(url));
}
catch (final URISyntaxException e) {
return null;
}
}
public @Nullable Object getContentOfUrl(final URI url) {
if (isDynamicUrl(url)) {
final String path= getDynamicPath(url);
final int targetEnd= path.indexOf('/', 1);
if (targetEnd > 1) {
final RequestInfo info= RHelpWebapp.extractRequestInfo(path.substring(targetEnd));
if (info != null) {
final REnv rEnv= this.rHelpManager.getREnv(info.rEnvId);
if (rEnv != null && info.cat == CAT_LIBRARY) {
final REnvHelp help= this.rHelpManager.getHelp(rEnv);
if (help != null) {
try {
final RPkgHelp pkgHelp= help.getPkgHelp(info.pkgName);
if (pkgHelp != null && info.cmd == RHelpWebapp.PKGCMD_HTML_PAGE) {
final RHelpPage page= pkgHelp.getPage(info.detail);
if (page != null) {
return page;
}
}
return pkgHelp;
}
finally {
help.unlock();
}
}
return null;
}
if (rEnv != null && info.cat == CAT_DOC) {
return new Object[] { rEnv, null };
}
return rEnv;
}
}
}
return null;
}
public boolean isPortableUrl(final URI url) {
return PORTABLE_URI_SCHEME.equals(url.getScheme());
}
public URI toHttpUrl(final URI url) throws URISyntaxException {
if (isPortableUrl(url)) {
final String path= nonNullElse(url.getPath(), "/"); //$NON-NLS-1$
final StringBuilder p= new StringBuilder(this.contextPath.length() + path.length() + 2);
p.append(this.contextPath);
if (path.isEmpty() || path.charAt(0) != '/') {
p.append('/');
}
p.append(path);
return createHttpUrl(p);
}
return url;
}
protected URI createPortableUrl(final String path) throws URISyntaxException {
return new URI(PORTABLE_URI_SCHEME, null, null, -1, path, null, null);
}
public @Nullable URI toPortableUrl(final URI url) throws URISyntaxException {
if (isDynamicUrl(url)) {
final String path= getDynamicPath(url);
return createPortableUrl(path);
}
else if (isPortableUrl(url)) {
return url;
}
return null;
}
public @Nullable URI toServerUrl(final URI url) {
if (isDynamicUrl(url)) {
final String path= getDynamicPath(url);
final int targetEnd= path.indexOf('/', 1);
if (targetEnd > 1
&& path.substring(1, targetEnd).equals(BROWSE_TARGET)) {
final int envIdEnd= path.indexOf('/', targetEnd + 1);
if (envIdEnd > targetEnd + 2) {
final REnv rEnv= this.rHelpManager.getREnv(path.substring(targetEnd + 1, envIdEnd));
if (rEnv != null) {
final REnvHelpConfiguration rEnvConfig= rEnv.get(REnvHelpConfiguration.class);
if (rEnvConfig != null
&& rEnvConfig.getStateSharedType() == REnvConfiguration.SHARED_SERVER) {
try {
final ServerClientSupport serverSupport= ServerClientSupport.getInstance();
return serverSupport.toServerBrowseUrl(rEnvConfig,
path.substring(envIdEnd) );
}
catch (final Exception e) {}
}
}
}
}
}
else {
final String path= url.getPath();
if (path != null && path.startsWith(RHelpWebapp.CONTEXT_PATH)) {
return url;
}
}
return null;
}
}