| /*=============================================================================# |
| # 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.internal.rhelp.core; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.status.ErrorStatus; |
| import org.eclipse.statet.jcommons.status.ProgressMonitor; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| |
| import org.eclipse.statet.internal.rhelp.core.index.REnvIndexReader; |
| import org.eclipse.statet.internal.rhelp.core.server.ServerClientSupport; |
| import org.eclipse.statet.rhelp.core.DocResource; |
| import org.eclipse.statet.rhelp.core.REnvHelp; |
| import org.eclipse.statet.rhelp.core.REnvHelpConfiguration; |
| import org.eclipse.statet.rhelp.core.RHelpCore; |
| import org.eclipse.statet.rhelp.core.RHelpKeywordGroup; |
| import org.eclipse.statet.rhelp.core.RHelpPage; |
| import org.eclipse.statet.rhelp.core.RHelpSearchQuery; |
| import org.eclipse.statet.rhelp.core.RHelpSearchRequestor; |
| 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 final class REnvHelpImpl implements REnvHelp { |
| |
| |
| public static final long NOT_AVAILABLE_STAMP= 0; |
| |
| public static long createStamp() { |
| long stamp= System.currentTimeMillis(); |
| if (stamp == NOT_AVAILABLE_STAMP) { |
| stamp++; |
| } |
| return stamp; |
| } |
| |
| |
| private final REnv rEnv; |
| |
| private final long stamp; |
| |
| private final @Nullable String docDir; |
| private final ImList<DocResource> manuals; |
| private final ImList<DocResource> miscRes; |
| |
| private final ImList<RHelpKeywordGroup> keywords; |
| |
| private final ImList<RPkgHelp> packages; |
| private @Nullable volatile Map<String, RPkgHelp> packageMap; |
| private @Nullable volatile REnvHelpIndex index; |
| |
| private boolean disposed; |
| |
| private final ReentrantReadWriteLock lock= new ReentrantReadWriteLock(); |
| |
| |
| public REnvHelpImpl(final REnv rEnv, final long stamp, |
| final @Nullable String docDir, final ImList<DocResource> manuals, final ImList<DocResource> miscRes, |
| final ImList<RHelpKeywordGroup> keywords, final ImList<RPkgHelp> packages) { |
| this.rEnv= rEnv; |
| this.stamp= stamp; |
| this.docDir= docDir; |
| this.manuals= manuals; |
| this.miscRes= miscRes; |
| this.keywords= keywords; |
| this.packages= packages; |
| } |
| |
| |
| public void dispose() { |
| this.lock.writeLock().lock(); |
| try { |
| this.disposed= true; |
| this.packageMap= null; |
| if (this.index != null) { |
| this.index.dispose(); |
| this.index= null; |
| } |
| } |
| finally { |
| this.lock.writeLock().unlock(); |
| } |
| } |
| |
| @Override |
| public REnv getREnv() { |
| return this.rEnv; |
| } |
| |
| public long getStamp() { |
| return this.stamp; |
| } |
| |
| |
| public @Nullable String getDocDir() { |
| return this.docDir; |
| } |
| |
| |
| public void lock() { |
| this.lock.readLock().lock(); |
| } |
| |
| @Override |
| public void unlock() { |
| this.lock.readLock().unlock(); |
| } |
| |
| |
| @Override |
| public ImList<DocResource> getManuals() { |
| return this.manuals; |
| } |
| |
| @Override |
| public ImList<DocResource> getMiscResources() { |
| return this.miscRes; |
| } |
| |
| @Override |
| public ImList<RHelpKeywordGroup> getKeywords() { |
| return this.keywords; |
| } |
| |
| @Override |
| public ImList<RPkgHelp> getPkgs() { |
| return this.packages; |
| } |
| |
| @Override |
| public @Nullable RPkgHelp getPkgHelp(final String pkgName) { |
| return getPackageMap().get(pkgName); |
| } |
| |
| private Map<String, RPkgHelp> getPackageMap() { |
| Map<String, RPkgHelp> map= this.packageMap; |
| if (map == null) { |
| synchronized (this) { |
| if (this.disposed) { |
| throw new IllegalStateException("This help index is no longer valid."); |
| } |
| |
| map= this.packageMap; |
| if (map == null) { |
| map= new HashMap<>(this.packages.size()); |
| for (final RPkgHelp pkgHelp : this.packages) { |
| map.put(pkgHelp.getName(), pkgHelp); |
| } |
| this.packageMap= map; |
| } |
| } |
| } |
| return map; |
| } |
| |
| synchronized void setIndex(final REnvHelpIndex index) { |
| if (!this.disposed && this.index == null) { |
| this.index= index; |
| } |
| } |
| |
| private REnvHelpIndex getIndex() { |
| REnvHelpIndex index= this.index; |
| if (index == null) { |
| synchronized (this) { |
| if (this.disposed) { |
| throw new IllegalStateException("This help index is no longer valid."); |
| } |
| |
| index= this.index; |
| if (index == null) { |
| final REnvHelpConfiguration rEnvConfig= this.rEnv.get(REnvHelpConfiguration.class); |
| if (rEnvConfig == null) { |
| throw new IllegalStateException("This R environment is no longer valid."); |
| } |
| try { |
| switch (rEnvConfig.getStateSharedType()) { |
| case REnvConfiguration.SHARED_DIRECTORY: |
| index= new REnvIndexReader(rEnvConfig); |
| break; |
| case REnvConfiguration.SHARED_SERVER: |
| index= ServerClientSupport.getInstance().getREnvHelpAccess(rEnvConfig); |
| break; |
| default: |
| throw new UnsupportedOperationException(rEnvConfig.getStateSharedType()); |
| } |
| } |
| catch (final Exception e) { |
| RHelpCoreInternals.log(new ErrorStatus(RHelpCore.BUNDLE_ID, |
| "An error occurred when initializing searcher for the R help index.", |
| e )); |
| throw new RuntimeException("An error occurred when reading R help index."); |
| } |
| this.index= index; |
| } |
| } |
| } |
| return index; |
| } |
| |
| @Override |
| public @Nullable RHelpPage getPage(final String pkgName, final String name) { |
| final RPkgHelp pkgHelp= getPackageMap().get(pkgName); |
| if (pkgHelp != null) { |
| return pkgHelp.getPage(name); |
| } |
| return null; |
| } |
| |
| @Override |
| public List<RHelpPage> getPagesForTopic(final String topic, |
| final @Nullable ProgressMonitor m) throws StatusException { |
| return getIndex().getPagesForTopic(topic, getPackageMap(), |
| 10, m ); |
| } |
| |
| |
| public @Nullable String getHtmlPage(final RPkgHelp pkgHelp, final String pageName, |
| final @Nullable String queryString) throws StatusException { |
| return getIndex().getHtmlPage(pkgHelp, pageName, queryString, |
| -1, null ); |
| } |
| |
| |
| public void search(final RHelpSearchQuery query, |
| final RHelpSearchRequestor requestor) throws StatusException { |
| getIndex().search(query, this.packages, getPackageMap(), requestor); |
| } |
| |
| } |