blob: d7ca0a8a42e34663ed271bbfc72f19348da0ec61 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.parsing.spi.indexing;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.modules.parsing.api.indexing.IndexingManager;
import org.netbeans.modules.parsing.impl.indexing.CancelRequest;
import org.netbeans.modules.parsing.impl.indexing.IndexFactoryImpl;
import org.netbeans.modules.parsing.impl.indexing.LogContext;
import org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater;
import org.netbeans.modules.parsing.impl.indexing.lucene.LayeredDocumentIndex;
import org.netbeans.modules.parsing.impl.indexing.lucene.LuceneIndexFactory;
import org.netbeans.modules.parsing.spi.indexing.support.IndexingSupport;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.util.Exceptions;
import org.openide.util.Parameters;
/**
* Represents a context of indexing given root.
* @author Tomas Zezula
*/
//@NotThreadSafe
public final class Context {
private final URL rootURL;
private final String indexerName;
private final int indexerVersion;
private final boolean followUpJob;
private final boolean checkForEditorModifications;
private final boolean sourceForBinaryRoot;
private final CancelRequest cancelRequest;
private final SuspendStatus suspendedStatus;
private final LogContext logContext;
private final Map<String,Object> props;
private final Callable<FileObject> indexBaseFolderFactory;
private FileObject indexFolder;
private boolean allFilesJob;
private FileObject root;
private FileObject indexBaseFolder;
private IndexingSupport indexingSupport;
private final IndexFactoryImpl factory;
Context (@NonNull final FileObject indexBaseFolder,
@NonNull final URL rootURL,
@NonNull final String indexerName,
final int indexerVersion,
@NullAllowed final IndexFactoryImpl factory,
final boolean followUpJob,
final boolean checkForEditorModifications,
final boolean sourceForBinaryRoot,
@NonNull final SuspendStatus suspendedStatus,
@NullAllowed final CancelRequest cancelRequest,
@NullAllowed final LogContext logContext
) throws IOException {
assert indexBaseFolder != null;
assert rootURL != null;
assert indexerName != null;
this.indexBaseFolderFactory = null;
this.indexBaseFolder = indexBaseFolder;
this.rootURL = rootURL;
this.indexerName = indexerName;
this.indexerVersion = indexerVersion;
this.factory = factory != null ? factory : LuceneIndexFactory.getDefault();
this.followUpJob = followUpJob;
this.checkForEditorModifications = checkForEditorModifications;
this.sourceForBinaryRoot = sourceForBinaryRoot;
this.cancelRequest = cancelRequest;
this.suspendedStatus = suspendedStatus;
this.logContext = logContext;
this.props = new HashMap<String, Object>();
}
Context (@NonNull final Callable<FileObject> indexBaseFolderFactory,
@NonNull final URL rootURL,
@NonNull final String indexerName,
final int indexerVersion,
@NullAllowed final IndexFactoryImpl factory,
final boolean followUpJob,
final boolean checkForEditorModifications,
final boolean sourceForBinaryRoot,
@NonNull final SuspendStatus suspendedStatus,
@NullAllowed final CancelRequest cancelRequest,
@NullAllowed final LogContext logContext
) throws IOException {
assert indexBaseFolderFactory != null;
assert rootURL != null;
assert indexerName != null;
this.indexBaseFolderFactory = indexBaseFolderFactory;
this.rootURL = rootURL;
this.indexerName = indexerName;
this.indexerVersion = indexerVersion;
this.factory = factory != null ? factory : LuceneIndexFactory.getDefault();
this.followUpJob = followUpJob;
this.checkForEditorModifications = checkForEditorModifications;
this.sourceForBinaryRoot = sourceForBinaryRoot;
this.cancelRequest = cancelRequest;
this.suspendedStatus = suspendedStatus;
this.logContext = logContext;
this.props = new HashMap<String, Object>();
}
// -----------------------------------------------------------------------
// Public implementation
// -----------------------------------------------------------------------
/**
* Returns the cache folder where the indexer may store language metadata.
* For each root and indexer there exist a separate cache folder.
* @return The cache folder
*/
public FileObject getIndexFolder () {
if (this.indexFolder == null) {
try {
final String path = getIndexerPath(indexerName, indexerVersion);
if (this.indexBaseFolder == null) {
try {
this.indexBaseFolder = this.indexBaseFolderFactory.call();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
if (this.indexBaseFolder == null) {
throw new IllegalStateException(
String.format(
"Factory %s returned null index base folder.", //NOI18N
this.indexBaseFolderFactory));
}
}
this.indexFolder = FileUtil.createFolder(this.indexBaseFolder,path);
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
}
return this.indexFolder;
}
/**
* Return the {@link URL} of the processed root
* @return the absolute URL
*/
public URL getRootURI () {
return this.rootURL;
}
/**
* Return the processed root, may return null
* when the processed root was deleted.
* The {@link Context#getRootURI()} can be used in
* this case.
* @return the root or null when the root doesn't exist
*/
public FileObject getRoot () {
if (root == null) {
root = URLMapper.findFileObject(this.rootURL);
}
return root;
}
/**
* Schedules additional files for reindexing. This method can be used for requesting
* reindexing of additional files that an indexer discovers while indexing some
* other files. The files passed to this method will be processed by a new
* indexing job after the current indexing is finished.
* That means that all the indexers appropriate
* for each file will have a chance to update their index. No timestamp checks
* are done on the additional files, which means that even files that have not been changed
* since their last indexing will be reindexed again.
*
* @param root The common parent folder of the files that should be reindexed.
* @param files The files to reindex. Can be <code>null</code> or an empty
* collection in which case <b>all</b> files under the <code>root</code> will
* be reindexed.
*
* @since 1.3
*/
public void addSupplementaryFiles(URL root, Collection<? extends URL> files) {
Logger repouLogger = Logger.getLogger(RepositoryUpdater.class.getName());
if (repouLogger.isLoggable(Level.FINE)) {
repouLogger.fine("addSupplementaryFiles: root=" + root + ", files=" + files); //NOI18N
}
RepositoryUpdater.getDefault().addIndexingJob(
root,
files,
true,
false,
false,
true,
true,
LogContext.create(LogContext.EventType.INDEXER, null, logContext).
withRoot(root).addFiles(files)
);
}
/**
* Indicates whether the current indexing job was requested by calling
* {@link #addSupplementaryFiles(java.net.URL, java.util.Collection) } method.
*
* @return <code>true</code> if the indexing job was requested by <code>addSupplementaryFiles</code>,
* otherwise <code>false</code>.
*
* @since 1.3
*/
public boolean isSupplementaryFilesIndexing() {
return followUpJob;
}
/**
* Indicates whether all files under the root are being indexed. In general indexing
* jobs can either index selected files under a given root (eg. when scheduled
* through {@link IndexingManager}) or they can index all files under the root. Some
* indexers are interested in knowing this information in order to optimize their
* indexing.
*
* @return <code>true</code> if indexing all files under the root.
*
* @since 1.6
*/
public boolean isAllFilesIndexing() {
return allFilesJob;
}
/**
* Indicates whether sources of some binary library are being indexed. Some
* indexers are interested in knowing this information in order to optimize their
* indexing.
*
* @return <code>true</code> if indexing sources for binary root.
*
* @since 1.17
*/
public boolean isSourceForBinaryRootIndexing() {
return sourceForBinaryRoot;
}
/**
* Notifies indexers whether they should use editor documents rather than just
* files. This is mostly useful for <code>CustomIndexer</code>s that may optimize
* their work and not try to find editor documents for their <code>Indexable</code>s.
*
* <p><code>EmbeddingIndexer</code>s can safely ignore this flag since they operate
* on <code>Parser.Result</code>s and <code>Snapshot</code>s, which are guaranteed
* to be in sync with editor documents or loaded efficiently from a file if the
* file is not opened in the editor.
*
* @return <code>false</code> if indexers don't have to care about possible
* editor modifications or <code>true</code> otherwise.
*
* @since 1.10
*/
public boolean checkForEditorModifications() {
return checkForEditorModifications;
}
/**
* Returns true if the indexing job is canceled either by external event like
* IDE exit or by a new indexing job which obscures the current one.
* The indexer should check the {@link Context#isCancelled()} in its index method
* and return as soon as possible if it returns true. The indexer factory should
* also check the {@link Context#isCancelled()} if it overrides the
* {@link SourceIndexerFactory#scanFinished(org.netbeans.modules.parsing.spi.indexing.Context)}
* method, when it's true the scanFinished should roll back all changes. The changes done into
* {@link IndexingSupport} are rolled back automatically.
*
* @return true if indexing job is canceled.
* @since 1.13
*/
public boolean isCancelled() {
return cancelRequest != null ?
cancelRequest.isRaised() :
false;
}
/**
* Returns {@link SuspendStatus} providing information
* about indexing suspension.
* @return the {@link SuspendStatus}
* @since 1.52
*/
@NonNull
public SuspendStatus getSuspendStatus() {
return suspendedStatus;
}
// -----------------------------------------------------------------------
// Package private implementation
// -----------------------------------------------------------------------
IndexFactoryImpl getIndexFactory() {
return this.factory;
}
String getIndexerName () {
return this.indexerName;
}
int getIndexerVersion () {
return this.indexerVersion;
}
void attachIndexingSupport(IndexingSupport support) {
assert this.indexingSupport == null;
this.indexingSupport = support;
try {
final LayeredDocumentIndex index = this.factory.getIndex(getIndexFolder());
if (index != null) {
index.begin();
}
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
}
IndexingSupport getAttachedIndexingSupport() {
return this.indexingSupport;
}
void clearAttachedIndexingSupport() {
this.indexingSupport = null;
}
void setAllFilesJob (final boolean allFilesJob) {
this.allFilesJob = allFilesJob;
}
void putProperty(
@NonNull String propName,
@NullAllowed final Object value) {
Parameters.notNull("propName", propName); //NOI18N
props.put(propName, value);
}
Object getProperty(@NonNull String propName) {
Parameters.notNull("propName", propName); //NOI18N
return props.get(propName);
}
static String getIndexerPath (final String indexerName, final int indexerVersion) {
final StringBuilder sb = new StringBuilder();
sb.append(indexerName);
sb.append('/'); //NOI18N
sb.append(indexerVersion);
return sb.toString();
}
}