org.springsource.ide.eclipse.commons.frameworks.core.async.FluxConstructorSearch.java Source code

Java tutorial

Introduction

Here is the source code for org.springsource.ide.eclipse.commons.frameworks.core.async.FluxConstructorSearch.java

Source

/*******************************************************************************
 * Copyright (c) 2016, 2018 Pivotal, 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:
 *     Pivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springsource.ide.eclipse.commons.frameworks.core.async;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
import org.eclipse.jdt.internal.core.search.IRestrictedAccessConstructorRequestor;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;

import reactor.core.publisher.Flux;
import reactor.core.publisher.ReplayProcessor;
import reactor.util.concurrent.Queues;

/**
 * 
 * NOTE:
 * This is derived from:
 * org.springsource.ide.eclipse.commons.frameworks.core.async.FluxJdtSearch
 * <p>
 * TODO:
 * There should only one reusable Flux JDT Search. Refactor so that FluxJdtSearch and FluxConstructorSearch
 * share a common base
 * <p>
 * Helper class to perform a search using Eclipse JDT search engine returning
 * the search results as a Flux.
 * <p>
 * The conversion from Eclipse callback style using {@link SearchRequestor} involves
 * a buffer that allows subscribers to attach to the Flux after the search has already
 * started without loosing results. However, if the buffer overflows then results will
 * be lost.
 * <p>
 * Clients should therfore start consuming the results as soon as possible and avoid
 * blocking the pipeline to avoid the loss of results they may care about.
 * <p>
 * Alternatively, client can specify a large enough buffer size so that the buffer can hold
 * at least as many results as the client may care to retrieve. This will allow the returned
 * Flux to be reused any number of times without timing constraints, provided that the
 * consumer never requests more than the number of buffered results.
 *
 * @author Kris De Volder
 */
@SuppressWarnings("restriction")
public class FluxConstructorSearch {

    private static final boolean DEBUG = ("" + Platform.getLocation()).contains("kdvolder");

    private static void debug(String string) {
        if (DEBUG) {
            System.out.println(string);
        }
    }

    private BasicSearchEngine engine = new BasicSearchEngine();
    private IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
    private String pattern = null;
    private int patternRule = SearchPattern.R_PATTERN_MATCH;
    private int bufferSize = Queues.SMALL_BUFFER_SIZE;
    private boolean useSystemJob = false;
    private int jobPriority = Job.INTERACTIVE;

    public FluxConstructorSearch engine(BasicSearchEngine engine) {
        this.engine = engine;
        return this;
    }

    public FluxConstructorSearch scope(IJavaSearchScope scope) {
        this.scope = scope;
        return this;
    }

    public FluxConstructorSearch bufferSize(int bufferSize) {
        this.bufferSize = bufferSize;
        return this;
    }

    public FluxConstructorSearch patternRule(int patternRule) {
        this.patternRule = patternRule;
        return this;
    }

    public FluxConstructorSearch pattern(String pattern) {
        this.pattern = pattern;
        return this;
    }

    /**
     * Implementation of {@link SearchRequestor} that emits search results to an {@link ReplayProcessor}
     * with replay capability.
     *
     * @author Kris De Volder
     */
    class FluxSearchRequestor {

        private boolean isCanceled = false;
        private ReplayProcessor<JavaConstructorHint> emitter = ReplayProcessor
                .<JavaConstructorHint>create(bufferSize);
        private Flux<JavaConstructorHint> flux = emitter.doOnCancel(() -> isCanceled = true);

        public Flux<JavaConstructorHint> asFlux() {
            return flux;
        }

        public void acceptSearchMatch(JavaConstructorHint match) {
            if (isCanceled) {
                debug("!!!! canceling search !!!!");
                //Stop searching
                throw new OperationCanceledException();
            }
            emitter.onNext(match);
        }

        public void cancel() {
            isCanceled = true;
        }

        public void done() {
            emitter.onComplete();
        }
    }

    public Flux<JavaConstructorHint> search() {
        validate();
        if (scope == null) {
            return Flux.empty();
        }
        final FluxSearchRequestor requestor = new FluxSearchRequestor();
        Job job = new Job("Search for " + pattern) {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                long start = System.currentTimeMillis();
                debug("Starting search for '" + pattern + "'");
                try {
                    engine.searchAllConstructorDeclarations(null, pattern.toCharArray(), patternRule, scope,
                            new IRestrictedAccessConstructorRequestor() {

                                @Override
                                public void acceptConstructor(int modifiers, char[] simpleTypeName,
                                        int parameterCount, char[] signature, char[][] parameterTypes,
                                        char[][] parameterNames, int typeModifiers, char[] packageName,
                                        int extraFlags, String path, AccessRestriction access) {
                                    requestor.acceptSearchMatch(JavaConstructorHint.asHint(modifiers,
                                            simpleTypeName, parameterCount, signature, parameterTypes,
                                            parameterNames, typeModifiers, packageName, extraFlags, path, access));
                                }

                            }, IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, monitor);
                    requestor.done();
                } catch (Exception e) {
                    debug("Canceled search for: " + pattern);
                    debug("          exception: " + ExceptionUtil.getMessage(e));
                    long duration = System.currentTimeMillis() - start;
                    debug("          duration: " + duration + " ms");
                    requestor.cancel();
                }
                return Status.OK_STATUS;
            }
        };
        job.setSystem(useSystemJob);
        job.setPriority(jobPriority);
        job.schedule();
        return requestor.asFlux();
    }

    private void validate() {
        Assert.isNotNull(engine, "engine");
        Assert.isNotNull(pattern, "pattern");
        Assert.isLegal(bufferSize > 0);
    }
}